TypeScript で class の constructor で前提条件を満たすようチェックを入れたい。
しかし、クラスがメソッドを持たない public メンバーのみで構成される場合、クラスのインスタンスでないオブジェトを代入できてしまう。
class Klass {
readonly val: number
constructor(val: number) {
if (val < 0) throw new Error("error")
this.val = val
}
}
function fn(k: Klass) {}
fn({ val: 1}) // ok
const k: Klass = { val: 1} // ok
色々調べたが、コンパイルオプションなどで検出することはできないようなので、自分なりに方法を考えてみた。
TL;DL
class にダミーのメンバを持たせることでコンパイル時に検出可能。
class Klass {
readonly val: number
private _: void
// 略
}
const k: Klass = { val: 1} // compile error
方法1: instanceof を使う
class Klass {
// 略
}
function fn(k: Klass) {
if (!(k instanceof Klass)) throw new Error("error")
}
fn(new Klass(1)) // oK
fn({ val: 1}) // runtime error
const k: Klass = { val: 1 } // ok
コンパイル時に検出されないし、代入は検出できないのでよろしくない
方法2: ダミーメンバを持たせる
class Klass {
readonly val: number
private _: void // 追加
constructor(val: number) {
this.val = val
}
}
function fn(k: Klass) {}
fn(new Klass(1)) // oK
fn({ val: 1}) // compile error
const k: Klass = { val: 1 } // compile error
/*
* Argument of type '{ val: number; }' is not assignable to parameter of type 'Klass'.
* Property '_' is missing in type '{ val: number; }' but required in type 'Klass'.
*/
コンパイル時に検出できる。生成されたJSにはメンバ _ は存在しないので、オーバーヘッドもなし。いい感じ。
ダミーメンバの型が void
なのは、'_' is declared but its value is never read.
を回避するため。