Programmingフロントエンド開発者

TypeScriptのクラスメソッドにおけるthisの型付けメカニズムは、通常の関数とアロー関数を使用する場合どうなっていますか?注意点とベストプラクティスについて説明してください。

Hintsage AIアシスタントで面接を突破

回答。

TypeScriptでは、クラス内のメソッドに対するthisの型はデフォルトで現在のクラスインスタンスを指します。しかし、アロー関数でメソッドを定義すると、thisのコンテキストは呼び出し時ではなく定義時にバインドされます。

通常のメソッド:

class Counter { value = 0; increment() { this.value++; } }

アロー関数:

class Counter { value = 0; increment = () => { this.value++; } }

注意点:

  • 通常のメソッドはコールバックとして渡すときにthisのコンテキストを失います(例:イベントリスナーで)。
  • アロー関数は定義されたクラスのコンテキストを保持しますが、プロトタイプには見えず、毎回のインスタンスのために作成されます。
  • メソッドのシグネチャでthisの型を明示的に指定して追加のチェックを行うこともできます。
class Foo { bar(this: Foo) { // ... } }

トリックのある質問。

increment()increment = () => {}の違いは何ですか?コールバックとして使用した場合、thisのコンテキストにどのように影響しますか?

間違った回答:

  • 「クラスのフィールドとメソッドには違いがなく、TypeScriptがすべて理解します。」

正しい回答:

  • 通常のメソッドでは、thisのコンテキストは呼び出し時に決定されます。メソッドを関数として渡すと、setTimeout(counter.increment, 0)のように、thisはundefined(strict mode)またはwindow(non-strict mode)になりますが、アロー関数はその周囲を保持します。
class Demo { value = 1; inc() { console.log(this.value); } incArrow = () => { console.log(this.value); } } const d = new Demo(); setTimeout(d.inc, 0); // undefined またはエラー setTimeout(d.incArrow, 0); // 1

このトピックを知らなかったことによる実際のエラーの例。


逸話

リアクティブフレームワークのプロジェクトでは、クラスメソッドが明示的なbindなしでコールバックとして直接渡されていました。その結果、thisはundefinedになり、thisプロパティにアクセスするときにアプリケーションがエラーでクラッシュしました。この問題は、メソッドをアロー関数に書き換えるか、明示的にbindすることで解決されました。


逸話

開発者はメソッド内でthisの型を明示したが、そのメソッド内のアロー関数の型付けを忘れました。その結果、ネストされたコールバック内のthisはクラスインスタンスではなくwindowを指していました。チームは状態漏れに直面し、イベントアーキテクチャを再構築する必要がありました。


逸話

大きなUIコンポーネントはアプリケーションを遅くしました。なぜなら、すべてのメソッドがクラスのアロー関数として記述されていたため(new Counter().increment = ...)、毎回新しい関数のコピーが各インスタンスのために作成され、通常のメソッドのようにプロトタイプでの1つの定義がなかったからです。その結果、メモリ使用量が増え、最適化が必要になりました。