Lazy Object Literal Initialization

Last updated 2 months ago

かなり一般的に、JavaScriptのコードベースでは、以下のようにしてオブジェクトリテラルを初期化します。

let foo = {};
foo.bar = 123;
foo.bas = "Hello World";

コードをTypeScriptに移すと即座に次のようなエラーが表示されます。

let foo = {};
foo.bar = 123; // Error: Property 'bar' does not exist on type '{}'
foo.bas = "Hello World"; // Error: Property 'bas' does not exist on type '{}'

これは、let foo = {}の状態から、TypeScriptはfooの型(初期化代入の左辺)を右辺{}の型に推論するためです(プロパティの無いオブジェクト)。そのため、あなたがTypeScriptが知らないプロパティに代入しようとするとエラーになります。

理想的な修正

TypeScriptでオブジェクトを初期化する適切な方法は、代入でそれを行うことです。

let foo = {
bar: 123,
bas: "Hello World",
};

これは、コードのレビューと保守性の意味でも適しています。

以下で説明する簡単な修正と、妥協的な遅延初期化パターンは、プロパティを初期化すること忘れることに苛まれます。

簡単な修正(Quick Fix)

TypeScriptに移行するJavaScriptコードベースが大きい場合は、理想的な修正が実行可能な解決策ではない可能性があります。その場合、型アサーションを慎重に使用してコンパイラをサイレントにすることができます:

let foo = {} as any;
foo.bar = 123;
foo.bas = "Hello World";

妥協(Middle Ground)

もちろん、anyアサーションを使用することは、TypeScriptの安全性を損なうため、非常に悪いことがあります。妥協的なfixは、インターフェースを作り、以下を確実にすることです。

  • 良いドキュメント

  • 安全な代入

これを以下に示します。

interface Foo {
bar: number
bas: string
}
let foo = {} as Foo;
foo.bar = 123;
foo.bas = "Hello World";

インターフェイスを使用するとバグから救われるという事実を示す簡単な例です:

interface Foo {
bar: number
bas: string
}
let foo = {} as Foo;
foo.bar = 123;
foo.bas = "Hello World";
// later in the codebase:
foo.bar = 'Hello Stranger'; // Error: You probably misspelled `bas` as `bar`, cannot assign string to number
}