TypeScript を書いていると「自分は型が分かっているのに、コンパイラは分かってくれない」という場面があります。そんなときに「この値はこの型だ」とコンパイラに伝えるのが型アサーション(as)と非 null アサーション(!)です。便利な一方で、使い方を誤ると型安全の網をすり抜けて実行時エラーの原因になります。この記事では、それぞれの基本的な使い方、よく使う場面、そして「使ってよいとき・避けるべきとき」の判断基準を初心者向けに解説します。
目次
型アサーション(as)とは
型アサーションは、値のうしろに as 型 と書いて「この値をこの型として扱ってほしい」とコンパイラに指示する構文です。型変換(キャスト)と似ていますが、実際にデータを変換するわけではなく、あくまでコンパイラへの型の伝達である点が重要です。実行時には何も起こりません。
// getElementById の戻り値は HTMLElement | null
// 「これは input 要素だ」と伝えると value を扱える
const input = document.getElementById('name') as HTMLInputElement;
console.log(input.value); // HTMLInputElement なので value が使える
document.getElementById() の戻り値は、より一般的な HTMLElement | null 型です。value プロパティは <input> 要素にしかないため、そのままでは使えません。ここで as HTMLInputElement と伝えることで、コンパイラはこの値を <input> として扱い、value へのアクセスを許可します。コンパイラよりも自分のほうが型を正確に知っているときに使うのが型アサーションです。
as には <HTMLInputElement>value という書き方(山かっこ構文)もありますが、これは JSX(React)の構文と衝突するため、現在は as 構文を使うのが一般的です。特に理由がなければ as で統一しましょう。
非 null アサーション(!)とは
非 null アサーションは、値のうしろに ! を付けて「この値は null や undefined ではない」とコンパイラに伝える構文です。TypeScript は安全のため「null かもしれない値」に対する操作を警告しますが、自分が null でないと確信できるときに ! でその警告を抑えられます。
function greet(name?: string) {
// name は string | undefined
// console.log(name.toUpperCase()); // エラー: undefined かもしれない
// 「ここでは必ず値がある」と伝える
console.log(name!.toUpperCase());
}
name! は「name は undefined ではない」という宣言です。これでコンパイラは toUpperCase() の呼び出しを許可します。ただし注意したいのは、! はチェックを消すだけで、実際に null でないことを保証するものではないという点です。もし本当に undefined が渡ってきた場合、実行時に「Cannot read properties of undefined」というエラーで落ちます。
アサーションに頼らず解決できないか考える
アサーションは「コンパイラのチェックを黙らせる」機能なので、多用するほど型安全のメリットが薄れます。多くの場合、アサーションの代わりにきちんとチェックを書くほうが安全です。先ほどの非 null アサーションの例は、if による絞り込みで書き直せます。
function greet(name?: string) {
// ! を使わず、値があるか確認してから使う
if (name) {
console.log(name.toUpperCase()); // ここでは name は string
return;
}
console.log('名前が指定されていません');
}
if (name) でチェックすれば、その分岐の中で name は string に絞り込まれ、! を使わずに安全に扱えます。しかも undefined だったときの処理も書けるので、実行時に落ちることもありません。DOM 要素の取得も同様に、if (input instanceof HTMLInputElement) のように確認してから使えば、as なしで型が絞り込まれます。まず「チェックで解決できないか」を考え、それが難しいときの最終手段としてアサーションを使うのがおすすめです。
型アサーションの2つの使い分けと注意点
as と ! は役割が違うので、次のように整理して覚えると混乱しません。
| 構文 | 伝える内容 | 主な用途 |
|---|---|---|
値 as 型 | この値はこの型だ | DOM 要素の具体化、JSON.parse の結果に型を付ける など |
値! | この値は null / undefined ではない | 初期化済みだとわかっている変数、確実に存在する要素へのアクセス |
また、as は何にでも変換できるわけではありません。まったく関係のない型へ直接変換しようとすると、TypeScript がエラーを出して止めてくれます。
const value = 'hello';
// エラー: string を number へは直接アサーションできない
// const n = value as number;
// どうしても必要なら unknown を経由する(安全ではないので要注意)
const n = value as unknown as number;
string を number へ直接 as で変換することはできません。これは「明らかに間違ったアサーション」を防ぐための安全装置です。as unknown as number のように unknown を経由すれば無理やり通せますが、これは型チェックを完全に無効化する危険な操作です。この二段アサーションを書きたくなったら、たいていは設計を見直すべきサインだと考えましょう。
まとめ
型アサーション as は「この値をこの型として扱ってほしい」、非 null アサーション ! は「この値は null / undefined ではない」とコンパイラに伝える構文です。どちらも実際に値を変換するわけではなく、あくまで型チェックへの指示にすぎません。そのため、間違った型を伝えれば実行時エラーにつながります。基本的には if や instanceof、typeof による型の絞り込みで解決できないかをまず考え、どうしてもコンパイラが型を推論できない場面でのみアサーションを使うのが安全です。as unknown as の二段変換は型安全を捨てる操作なので、避けられないか一度立ち止まって検討しましょう。「アサーションは便利な近道だが、責任は自分が負う」という意識で使い分けてください。