こんにちは、清水です。
先月、Angularでstrict: trueにした。(strictNullChecks, strictPropertyInitialization編)
ていうブログを書きましたが、その完結編です。
strict: true になりました
無事にstrict: true
にする対応終了しました👏平成最後にできてよかったです
前回書いたstrictNullChecks, strictPropertyInitializationの対応を終えると、初期値や型を整理したプロパティを使用している側の修正がメインとなりました。
どんな箇所でエラーがよく出ていたかを紹介します。
null,undefinedの使い方が整理されてない
name?: string;
selectedId: string | null = null;
constructor(hogeService: HogeService) {
this.hogeService.get<Hoge>(this.selectedId) // TS2345 Argument of type 'string | null' is not assignable to parameter of type 'string'.
.subscribe((res) => {
this.name = this.getFullName(res); // TS2322 Type 'string | null' is not assignable to type 'string | undefined'.
});
}
getFullName(resource?: { last_name: string, first_name: string }): string | null {
if (!resource) { return null; }
...
}
もともとプロパティの初期値が整理されていなかったので、当然プロパティを使う側の処理のnull, undefinedの使い方もバラバラでした。
上の例だと、次のようなことが起きてしまっています。
- HogeService#getはstring型の引数を期待しているけどselectedIdはnullableである
- nameはオプショナルだけど、getFullNameの戻り値がstring | null になってしまっている
このような整理されてない処理を、プロパティの型を元に改修しました。
オプショナル, nullableなプロパティへのアクセス
resource: { name: string } | null = null;
resources: string[] = [];
max?: number;
selectedNumber?: number;
this.resource.name // TS2531 Object is possibly 'null'
this.max > 0 // TS2532 Object is possibly 'undefined'
this.resources[selectedNumber] // TS2538 Type 'undefined' cannot be used as an index type
値がない場合の考慮漏れが多々あったので、条件分岐の追加やデフォルト値の設定をしました。
条件分岐処理での考慮漏れ
let amount: { total: number, consumption_tax: number };
let summary: number;
switch (this.type) {
case 'car':
amount = { total: 1000, consumption_tax: 80 };
break;
case 'house':
amount = { total: 2000, consumption_tax: 160 };
break;
}
summary = amount.total + amount.consumption_tax; // TS2454 'amount' is used before being assigned.
上の例だと、typeが 'car' 'house' 以外の場合の考慮漏れがありTS2454のエラーが出ます。
switch文での改修ではdefault節を追加して対応しました。
このようなエラーが事前にわかるとバグを防いでくれていいなと感じました。
RxJsを5から6にした時の修正もれ
import { Observable } from 'rxjs/Rx'; // TS2305 Module '"...../rxjs/Rx"'has no exported member 'Observable'.
Observable.timer(1).subscribe(() => { ... }); // TS2305 Property 'timer' does not exist on type 'typeof Observable'
strict: trueにしてよかったこと
不要なソースを消すことができた
エラー改修をするにあたり全体ソースを見直すことになったので、使ってないプロパティや不要な処理を大幅に消せました。
null, undefinedの整理
プロパティの初期値、それらを使う側での処理でnull, undefinedの使い分けが実装者の感覚で行われてしまっていた部分を整理することができました。
バグを埋め込みにくいソースになった
一番よかったのが、これです。
プロパティが意図しない使われ方をされにくくなったり、値がない場合の考慮漏れや条件分岐処理での漏れの検出が事前にできるようになったりしたため、バグを埋め込みにくい環境になりました。
また、エラー改修するうえで既存バグの検出&修正もできました。
まとめ
今だとAngular CLIで作成したprojectではデフォルトでstrict: true
になっていますが、
まだfalseの人は早めに変更した方がいいと思います。 (←追記:すみません勘違いしてました。。)
strict対応をしようとしている人は、なるべく早くに対応したほうがいいと思います。
ただ、修正にはある程度工数がかかります。(ソース量にもよりますが)
ある箇所のエラーを直すと別な箇所で新たなエラーがでるといったぐあいなので、見積もりをする際にはきちんとバッファを積み余裕を持って修正を行うことをお勧めします。(ちなみに私は見積もりが甘すぎて辛い思いをしました笑)
あとは、null, undefinedの使い分けなどのチーム内の方針となるような部分は、チーム内で認識を合わせてながら改修を進めていくとやりやすかったです。