TypeScript のコンパイルエラーを一時的に抑止したい場面は多々ある。この記事では、自分が型エラーを回避するのに便利だなと思っている機能を状況に応じて 3 つ紹介したいと思う。想定している文脈は、趣味プロジェクトで、フレームワークを使わない素のフロントエンド開発。
Type Guards
状況: このエレメントは<foo>
なんだからbar
っていう属性があるのに TypsScript はそれを分かってくれない。
例えばオーディオを再生するページを静的に記述したとする。
<audio id="my-audio"></audio>
このオーディオに対して再生位置をリセットするスクリプトを書きたい。書いている側からするとmy-audio
はHTMLAudioElement
であることが分かっている。getElementById('my-audio')
の返り値はHTMLAudioElement
だからと思って以下のように書くとコンパイラに怒られる。
const audioEl = document.getElementById("my-audio");
// NG: `[ts] Property 'currentTime' does not exist on type 'HTMLElement'. [2339]`
audioEl.currentTime = 0;
この場合は Type guards に頼る。if 文で型の整合性をチェックすると、コンパイラがコントロールフローを解析して型を限定してくれる。以下では if 文以降audioEl
はHTMLMediaElement
であることが保証される。
const audioEl = document.getElementById("my-audio");
if (!(audioEl instanceof HTMLMediaElement)) {
throw new Error("#my-audio is not an HTMLMediaElement");
}
// OK: At this point TS compiler knows `audioEl` is an HTMLMediaElement.
audioEl.currentTime = 0;
冗長だけど if 文以降に型チェックの恩恵を受けられることを考えるとトレードオフとしては悪くない。as
を使う方法もあるけれど、 Type Guards を使ったほうがより安全になる。
参考: Advanced Types · TypeScript
Non Null Assertion Operator
状況: 関数foo()
はnull
やundefined
じゃない値を返すのが分かってるのに TypeScript がそれを分かってくれない。
2D の絵を描きたいとする。ブラウザ上で 2D の絵を描くにはHTMLCanvasElement
を用意してそれに対してgetContext('2d')
を呼んで描画コンテキストを取得する。だけど TypeScript はgetContext()
はnull
を返すかもしれないと文句を言ってくる。
// NG: [ts] Type 'CanvasRenderingContext2D | null' is not assignable to type 'CanvasRenderingContext2D'.
// Type 'null' is not assignable to type 'CanvasRenderingContext2D'. [2322]
const ctx: CanvasRenderingContext2D = canvas.getContext("2d");
この場合は!
を末尾につけている。これはNon Null Assertion Operatorというやつで、式の末尾に!
をつけるとコンパイラはその式がnull
やundefined
を返すことがないと仮定するようになる。null
やundefined
を返す式にしか使えないけど、Type guards を使うよりも簡潔に型を限定できる。
// OK
const ctx: CanvasRenderingContext2D = canvas.getContext("2d")!;
ctx.clearRect(0, 0, width, height);
ただ Type guards と違ってコンパイラが null や undefined にならないことを保証してくれるわけでは無い、という点に注意。
参考: TypeScript 2.0 · TypeScript
@ts-ignore
状況: window
に一時的にデバッグ用のプロパティを追加したいけど TypeScript がそれを許してくれない。
開発の初期段階ではブラウザのデベロッパーツールを使っていろんな検証をしたい。例えば自作のApp
オブジェクトの状態をデベロッパーツールで確認したいとする。そんなときには手っ取り早くアクセスできるオブジェクト、例えばwindow
にそのオブジェクトをぶら下げるのが簡単だろう。でも単純にそれをやろうとするとコンパイラが怒る。
const app = new App(...);
// NG: [ts] Property 'app' does not exist on type 'Window'. [2339]
window.app = app;
こういう状況では@ts-ignore
を使っている。@ts-ignore
をコメントとして書くと、以後の一文だけはコンパイラは何もエラーを出さなくなる。tsconfig.json などで一括にエラーを抑止するのは避けたいけど、この一文だけ見逃して欲しい場面で重宝する。
const app = new App(...);
// @ts-ignore
window.app = app; // OK
...
console.log(window.app); // NG
あくまで@ts-ignore
の直下の行だけエラーを出力しないようになるだけなので、ほかの場所でapp
を使おうとするとコンパイラに怒られる。自分は@ts-ignore
をDevToolsを使ったデバッグや調査をしたいときや、トリッキーなimport
をしている場所のエラーを抑止したいときなんかに使っている。
参考: TypeScript 2.6 · TypeScript
参考文献
Revised Revised 型の国のTypeScript は非常に良い入門書。一日ぐらいかけて目を通しておくと TypeScript の気持ちがわかるようになると思う。自分は技術書典3で紙の本を購入した。
TypeScriptのunsafeな操作まとめ では TypeScript の型検査が常に有効ではないことを議論している。