TypeScriptで関数のthisの型注釈をする
はじめに
vueを使ってTypeScriptに入門しているときに以下の問題にあたった。
forum.vuejs.org stackoverflow.com
そもそもthisの型注釈の機能を知らなかったので、公式ドキュメントを読みながらthisの型注釈についてインプットすることにした。
- 型注釈によって関数のthisの型を指定することができる
- thisの型注釈が必要なケース
型注釈によって関数のthisの型を指定することができる
以下のようなコードを考える。
class C1 { name: string; constructor(name: string) { this.name = name; } say() { console.log(this.name) } }
say
メソッドの中で呼ばれているthis
の型はthis
になり、C1
のサブクラスでも辻褄が合うような扱いになる。
ただし、以下のような形でthis
の型を明示することができる。
class C1 { // ... say(this: C1) { console.log(this.name) } }
C1
を指定した場合は挙動は問題なさそうだが、void
などを指定するとコンパイルエラーになる。
なぜこのような機能があるのか。
thisの型注釈が必要なケース
noImplicitThisを有効にしている場合に必要になることがある。 以下のようなコードを考える。
const execCallback = (fn: () => void) => { fn() } class C1 { name: string; constructor(name: string) { this.name = name; } say() { execCallback(function() { console.log(this.name) }) } }
say
メソッドの中で console.log(this.name)
を呼ぶ関数を execCallback
に渡しているが、関数式で定義した関数のthis
は実行時に決まるため、say
メソッド内で見ているthis
はany型になってしまう。結果としてnoImplicitThisに引っかかるのである。警告を外す方法としてはsay
メソッドの中のfunction() { ... }
をfunction(this: any) { ... }
とすれば警告を解除できるが、残念ながら実行時に落ちてしまう。。。結局 function
は使わず。アロー関数を使うのが今回の正解だ。
ただしアロー関数を使えないケースもある。「はじめに」の中で書いたvueのmethod内で _.debounce
を使う例がわかりやすい。(この例もアロー関数を使えるのかもしれない。)
vueのforumにもあるように、このケースは妥協してany
を使うのも良さそうだ。
まとめ
TypeScriptに入門していたところ早速本に出ていない内容が出てきたが、ちゃんとvueのforumやstackoverflow、そして何より公式ドキュメントにthisの型注釈について書いてあったのが良かった。