以前 C#erから見たTypeScriptの罠 という記事を書きましたが、その後 TypeScript 2.7 から strictPropertyInitialization による初期化漏れチェックが可能となり(参考:TypeScript 2.7.1 変更点)、TypeScriptはますます型安全な言語となりました。
private str: string;
public constructor() {
//ここでstrを初期化しないとコンパイルエラーになる
//this.str = "hoge";
}
「初期化の強制って気持ち悪くない?」
しかし、Javaなどに慣れ親しんだ人たちからは、こんな声があがるかもしれません。
初期化を強制されるって、なんかヌルポを避けるためにとりあえず初期値つっこんでおくバッドノウハウみたいで気持ち悪くない?と。
//Javaとかで見る、Null例外だけ避けようとして適当な初期値を入れるやつ
private String str = "";
private int[] nums = new int[0];
そんなときは、nullとの共用型にすることによって、nullを許容する変数を作ることができます。
private str: string | null = null;
それをやったら、Null安全じゃなくなっちゃうんじゃないの?と思われるかもしれませんが、これはわりと安全なんです。
Type GuardでNull安全
先に書いたnull許容の変数に対し、ノーチェックでstring型のメンバ(lengthなど)を参照しようとすると、stringかnullかが確定していないため、コンパイルエラーになります。
private str: string | null = null;
public getLength(): number {
//エラーになる
return this.str.length;
}
TypeScriptにはType Guardという仕組みがあり、型チェックのif文を作ることによって、以降のルートでは、型を確定した上でコンパイルチェックできるようになります。
つまり先ほどの例は、以下のように書くことで利用可能となります。
private str: string | null = null;
public getLength(): number {
if (this.str != null) {
//ここではstringであることが確定する
return this.str.length;
}
return 0;
}
使う前にNullチェックを強制される。
お気づきかもしれませんが、これはまさしく他の言語のMaybe/Optionalに相当するものですね。
ちなみに、以下のように疑似的なNull合体演算子を使って短く書くことも可能です。
private str: string | null = null;
public getLength(): number {
return (this.str || "").length;
}
まとめ
以上のように、strictPropertyInitializationを有効にしたからといって不便が生じることはありません。積極的に利用しましょう。
strictオプションをtrueにしておけばstrictPropertyInitializationも有効になりますし、strictオプションをtrueにしないことはまずないでしょうから、通常あまり意識せずとも利用することになると思いますが。