🐟

[Angular][TypeScript][エラー] no initializer and is not definitely

2023/03/30に公開

やりたいこと

@ViewChildでプロパティを初期化したい。

@ViewChild('tabs') tabGroup: MatTabGroup;

エラー内容

Property 'tabGroup' has no initializer and is not definitely assigned in the constructor.ts(2564)

エラーの内容としては、undefinedを許容していないプロパティに対して、プロパティが宣言時やコンストラクタで初期化されていないことを指摘されています。

TypeScriptのクラスプロパティの初期化チェックをするstrictPropertyInitializationオプションによるものです。

原因

今回の@ViewChildはAngularのデコレータであり、プロパティtabGroupの初期化のタイミングは、クラス初期時ではなく、ビューの解決後になるため、それまでは必ずundefinedになってしまいます。

解決策

今回の@ViewChildの場合、上記と通り初期化タイミングの問題で?でオプショナルプロパティにするか、| undefinedundefinedを許容する必要があります。

@ViewChild('tabs') tabGroup?: MatTabGroup;

ただ、このプロパティを使うときは初期化タイミング上、ngAfterViewInitで参照する必要がありますが、それ以外の場合、!(非nullアサーション)を使います。

また、@ViewChildなどのデコレータではなく、下記のような通常のプロパティ定義のときも同様のエラーが出ます。

export class ExampleComponent implements OnInit {
  age: number;
}

この場合は、3つの解決法があります。

  1. コンストラクタで初期化
  2. 初期化子で初期化
  3. ?オプショナルプロパティでundefinedを許容する

また、@Inputデコレータ使用時は、プロパティが必須の時とそうでないときがあります。

?オプショナルプロパティでも解決できますが、親から必ず値が渡されるプロパティについては!(非nullアサーション)を使ってundefinedを許容することを明示します。

@Input() name!: Person;

安易に!(非nullアサーション)を使うべきではありませんが、@Input()の場合はこれが一番分かりやすいと思います。

明確な割り当てアサーションと非Nullアサーションは、型の安全性を保証する責任をコンパイラからプログラマに移すものです。そして、型に関してはコンパイラより人間のほうがミスをしやすいです。なので、こうしたアサーションはできる限り使わないほうが安全性は高いです。
https://typescriptbook.jp/reference/values-types-variables/definite-assignment-assertion#明確な割り当てアサーションを使う

参考

https://lacolaco.hatenablog.com/entry/2018/06/27/125101

https://typescriptbook.jp/reference/values-types-variables/definite-assignment-assertion#明確な割り当てアサーションを使う

https://typescriptbook.jp/reference/tsconfig/strictpropertyinitialization

Discussion