【TypeScript】非nullアサーション演算子(!)とは?使い方や注意点を解説!

TypeScriptのコードで「el!.innerHTML = 'hogehoge';」のように、変数のあとに「ビックリマーク(!)」がついているのを見たことはありませんか?

この!非nullアサーション演算子と呼ばれる記号です。

TypeScriptでは、ある変数がnullundefinedかもしれないとき、エラーを出して型安全を守ってくれます。でも、「この変数は絶対に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では、型安全を守るために、変数がnullundefinedの可能性がある場合、「この変数はnullundefinedかもしれないよ!」というエラーを出してくれます。

一方、変数の後に非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.currentnullではないはずです。そのため、開発者は「絶対にnullじゃない」と確信して非nullアサーション演算子(!)を使い、TypeScriptのエラーを回避しています。

非nullアサーション演算子(!)の注意点

非nullアサーション演算子(!)の使用には注意が必要です。非nullアサーション演算子(!)はあくまでコンパイル時のチェックをパスさせるためのものであり、実行時にnullundefinedが入らないことを保証するわけではありません。実行時にnullまたはundefinedが発生すると、エラーでクラッシュする可能性があります。以下にサンプルコードを以下に示します。

const value: string | null = null;

// 実行時に「Cannot read properties of null (reading 'length')」というエラーが発生して、アプリケーションがクラッシュします。
console.log(value!.length);

上記のサンプルコードでは、valuestringnullのどちらかを取る変数です(今回は明示的にnullが代入されています)。

しかし、次の行でvalue!と非nullアサーション演算子(!)を使って、「このvalueは絶対にnullでもundefinedでもない」とTypeScriptに主張しています。その結果、コンパイル時のエラーは出ませんが、実際にプログラムを実行すると、valuenullのままなのでエラーになります。

nullアサーション演算子は「この変数は絶対にnullでもundefinedでもない」という強い確信があるときだけ使うようにしましょう。

非nullアサーション演算子(!)とオプショナルチェーン(?.)の違い

「非nullアサーション演算子(!)」と「オプショナルチェーン(?.)」は、どちらも「nullundefinedの可能性」に関わる文法ですが、目的と動作がまったく異なります。以下に違いを示します。

比較項目非nullアサーション演算子(!オプショナルチェーン(?.
意味「絶対にnullundefinedじゃない」と断言するnullundefinedの可能性を考慮して安全にアクセスする
書き方value!.propertyvalue?.property
valuenullの場合実行時エラー(クラッシュ)undefinedが返る(エラーにならない)
valueundefinedの場合実行時エラー(クラッシュ)undefinedが返る(エラーにならない)
型チェックTypeScriptの型エラーを無視して突破するTypeScriptの型システムに従って安全に評価する
よく使う場面「この時点では絶対に存在する」と確信があるとき「存在しないかもしれないけど、あればアクセスしたい」とき
安全性低い(valuenullundefinedだとクラッシュする)高い(valuenullundefinedでもクラッシュしない)

以下に「非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
    • valuenullなのに「絶対ある!」と主張してアクセスするため、Cannot read properties of null (reading 'message')というエラーが発生してクラッシュします。
  • value?.message
    • valuenullなのでundefinedを返して安全に処理されます(エラーにはなりません)。

あわせて読みたい

オプショナルチェーン(?.』については下記の記事で詳しく説明しています。興味のある方は下記のリンクからぜひチェックをしてみてください。

本記事のまとめ

この記事では「非nullアサーション演算子(!」について、以下の内容を説明しました。

  • 非nullアサーション演算子(!)とは?
  • 非nullアサーション演算子(!)の用途
  • 非nullアサーション演算子(!)の注意点
  • 非nullアサーション演算子(!)とオプショナルチェーン(?.)の違い

非nullアサーション演算子(!)は、TypeScriptの型チェックを一時的に無視して「この値は絶対にnullundefinedじゃない!」と主張するための記号です。

使いどころを間違えなければ便利ですが、実行時のクラッシュリスクもあるため、使用には注意が必要です。特にDOM操作やReactのようなフレームワークのライフサイクルを理解したうえで、「この時点では絶対に存在する」と確信できる場合にだけ使うようにしましょう。

一方で、「nullundefinedの可能性があるけれど、あればアクセスしたい」という場面では、オプショナルチェーン(?.)を使うことで、安全にコードを書くことができます。

お読み頂きありがとうございました。

スポンサーリンク