TypeScriptのコードで「el!.innerHTML = 'hogehoge';
」のように、変数のあとに「ビックリマーク(!
)」がついているのを見たことはありませんか?
この!
は非nullアサーション演算子と呼ばれる記号です。
TypeScriptでは、ある変数がnull
やundefined
かもしれないとき、エラーを出して型安全を守ってくれます。でも、「この変数は絶対にnull
でもundefined
でもない」とわかっている場面もありますよね。そんなときに使えるのが「非nullアサーション演算子(!
)」なんです。
この記事では「非nullアサーション演算子(!
)」について、以下の内容をサンプルコードを用いてわかりやすく解説します。
- 非nullアサーション演算子(
!
)とは? - 非nullアサーション演算子(
!
)の用途 - 非nullアサーション演算子(
!
)の注意点 - 非nullアサーション演算子(
!
)とオプショナルチェーン(?.
)の違い
非nullアサーション演算子(!)とは?
非nullアサーション演算子(!
)は、TypeScriptの「型アサーション」の一種で、型システムに「この変数は絶対にnull
でもundefined
でもない」と開発者がアサート(主張)するための記号です。
以下に非nullアサーション演算子(!
)を用いたサンプルコードを示します。
// document.getElementByIdの戻り値の型は「HTMLElement | null」
const el = document.getElementById('btn');
// 非nullアサーション演算子を用いない場合、elがnullの可能性があるためエラーになる
el.innerHTML = 'Click me'; // 'el' is possibly 'null'('el' は 'null' の可能性があります。)
// 非nullアサーション演算子でnullでないと主張すると、エラーにならない
el!.innerHTML = 'Click me';
TypeScriptでは、型安全を守るために、変数がnull
やundefined
の可能性がある場合、「この変数はnull
やundefined
かもしれないよ!」というエラーを出してくれます。
一方、変数の後に非nullアサーション演算子(!
)を付けて、「この変数はnull
でもundefined
でもない」と主張すると、エラーにならなくなります。
非nullアサーション演算子は英語では「Non-null assertion operator」と書きます。
非nullアサーション演算子(!)の用途
DOM要素を取得したとき
document.getElementById
の戻り値の型は「HTMLElement | null
」なので、直接プロパティにアクセスするとエラーになります。でも、「この要素は絶対に存在する」とわかっているなら、非nullアサーション演算子(!
)を使って「null
じゃない」と伝えられます。以下にサンプルコードを示します。
const el = document.getElementById('btn');
el!.innerHTML = 'Click me';
または
const el = document.getElementById('btn')!;
el.innerHTML = 'Click me';
el!
のように、アクセス時に毎回!
をつけると可読性が下がります。この場合、document.querySelector("button")!;
のように最初に !
を使って代入しておけば、その後は普通に使えて見やすくなります。
このように非nullアサーション演算子(!
)は、後ろにメソッドやプロパティ等が続く必要がある訳ではありません。後ほど説明するオプショナルチェーン(?.
)では後ろにメソッドやプロパティ等が必須です。
ライブラリやフレームワーク使用時(Reactなど)
ReactやVueなどのフレームワークでは、あるタイミングになれば必ず値が入るという場面がよくあります。でも、TypeScriptはそれをうまく理解できず、「null
かもしれないよ」と警告してくることがあります。そんなときに使えるのが、非nullアサーション演算子(!
)です。
以下にサンプルコードを示します。
import { useRef } from 'react';
function App() {
// useRefでrefオブジェクト(ここではtextEl)を作成し、初期値をnullとする
const textEl = useRef<HTMLParagraphElement>(null);
const onButtonClick = () => {
// 非nullアサーションが必要(textEl.current は null の可能性があるため)
textEl.current!.style.color = 'red';
};
return (
<>
<p ref={textEl}>Hello</p>
<button onClick={onButtonClick}>Helloを赤色に変える</button>
</>
);
}
export default App;
textEl.current
は型としてHTMLParagraphElement | null
なので、null
の可能性があります。しかし、onButtonClick
が実行される頃には<p ref={textEl}>
がすでに画面上に描画されているため、textEl.current
はnull
ではないはずです。そのため、開発者は「絶対にnull
じゃない」と確信して非nullアサーション演算子(!
)を使い、TypeScriptのエラーを回避しています。
非nullアサーション演算子(!)の注意点
非nullアサーション演算子(!
)の使用には注意が必要です。非nullアサーション演算子(!
)はあくまでコンパイル時のチェックをパスさせるためのものであり、実行時にnull
やundefined
が入らないことを保証するわけではありません。実行時にnull
またはundefined
が発生すると、エラーでクラッシュする可能性があります。以下にサンプルコードを以下に示します。
const value: string | null = null;
// 実行時に「Cannot read properties of null (reading 'length')」というエラーが発生して、アプリケーションがクラッシュします。
console.log(value!.length);
上記のサンプルコードでは、value
はstring
かnull
のどちらかを取る変数です(今回は明示的にnull
が代入されています)。
しかし、次の行でvalue!
と非nullアサーション演算子(!
)を使って、「このvalue
は絶対にnull
でもundefined
でもない」とTypeScriptに主張しています。その結果、コンパイル時のエラーは出ませんが、実際にプログラムを実行すると、value
はnull
のままなのでエラーになります。
非null
アサーション演算子は「この変数は絶対にnull
でもundefined
でもない」という強い確信があるときだけ使うようにしましょう。
非nullアサーション演算子(!)とオプショナルチェーン(?.)の違い
「非nullアサーション演算子(!
)」と「オプショナルチェーン(?.
)」は、どちらも「null
やundefined
の可能性」に関わる文法ですが、目的と動作がまったく異なります。以下に違いを示します。
比較項目 | 非nullアサーション演算子(! ) | オプショナルチェーン(?. ) |
---|---|---|
意味 | 「絶対にnull やundefined じゃない」と断言する | null やundefined の可能性を考慮して安全にアクセスする |
書き方 | value!.property | value?.property |
value がnull の場合 | 実行時エラー(クラッシュ) | undefined が返る(エラーにならない) |
value がundefined の場合 | 実行時エラー(クラッシュ) | undefined が返る(エラーにならない) |
型チェック | TypeScriptの型エラーを無視して突破する | TypeScriptの型システムに従って安全に評価する |
よく使う場面 | 「この時点では絶対に存在する」と確信があるとき | 「存在しないかもしれないけど、あればアクセスしたい」とき |
安全性 | 低い(value がnull やundefined だとクラッシュする) | 高い(value がnull やundefined でもクラッシュしない) |
以下に「非nullアサーション演算子(!
)」と「オプショナルチェーン(?.
)」の違いがわかるサンプルコードを以下に示します。
function test(value: { message: string } | null | undefined) {
// 非nullアサーション演算子(!)を使用した場合
console.log("非nullアサーション:", value!.message);
// オプショナルチェーン(?.)を使用した場合
console.log("オプショナルチェーン:", value?.message);
}
test(null);
// 非nullアサーション演算子(!)の箇所で「Cannot read properties of null (reading 'message')」というエラーが発生
value!.message
value
がnull
なのに「絶対ある!」と主張してアクセスするため、Cannot read properties of null (reading 'message')
というエラーが発生してクラッシュします。
value?.message
value
がnull
なのでundefined
を返して安全に処理されます(エラーにはなりません)。
あわせて読みたい
『オプショナルチェーン( 続きを見る?.
)』については下記の記事で詳しく説明しています。興味のある方は下記のリンクからぜひチェックをしてみてください。 オプショナルチェーン(はてなドット?.)とは?「使い方」などを解説!
本記事のまとめ
この記事では「非nullアサーション演算子(!
)」について、以下の内容を説明しました。
- 非nullアサーション演算子(
!
)とは? - 非nullアサーション演算子(
!
)の用途 - 非nullアサーション演算子(
!
)の注意点 - 非nullアサーション演算子(
!
)とオプショナルチェーン(?.
)の違い
非nullアサーション演算子(!
)は、TypeScriptの型チェックを一時的に無視して「この値は絶対にnull
やundefined
じゃない!」と主張するための記号です。
使いどころを間違えなければ便利ですが、実行時のクラッシュリスクもあるため、使用には注意が必要です。特にDOM操作やReactのようなフレームワークのライフサイクルを理解したうえで、「この時点では絶対に存在する」と確信できる場合にだけ使うようにしましょう。
一方で、「null
やundefined
の可能性があるけれど、あればアクセスしたい」という場面では、オプショナルチェーン(?.
)を使うことで、安全にコードを書くことができます。
お読み頂きありがとうございました。