【JavaScript/TypeScript】?. と !. の違い

副業先のフロントエンド開発で!.を初めて見て「?.は知っているけど!.は知らないぞ?」となったのでこの機会に両者の特徴や違いをまとめておこうと思う。

まず、両者の演算子の読み方?名前?は以下のとおり。

  • ?.:オプショナルチェーン演算子
  • !.:非nullアサーション演算子

タイトルには「違い」と書いているが直接的な比較ではなく、それぞれが「どんなものなのか?」についてまとめる記事にする。

目次

?.(オプショナルチェーン演算子)

?.オプショナルチェーン演算子と言う。もしくはオプショナルチェイニングと呼ばれていたりするのでどちらでも良いかと思う。(どちらでも通じる)

ちなみにこれはTypeScriptではなくJavaScriptの機能。

この演算子の機能はオブジェクトやクラスのプロパティにアクセスしようと思った時にそのプロパティがnullishな値の場合、?.時点でコードの実行を終了しundefinedを返却する。

nullishは「null または undefined」のこと。

極端な例だがサンプルコードを書いてみる。

let org;

console.log(org); // undefined
console.log(org.length); // error: Uncaught TypeError: Cannot read properties of undefined (reading 'length')
console.log(org?.length); // undefined

変数orgはundefinedと定義されているので、.lengthで文字列の長さを取得しようとするとエラーになる。(文字列型のデータじゃ無いので当たり前)

ただ、?.を使うとエラーにはならず、undefinedを返却する。

これはTypeScriptの領域になるが、オプショナルチェーン演算子が入った式を変数に格納した場合、その変数のデータ型は必ず○○ | undefinedになる。

type User = {
	age: number
	name?: string
}

const user: User = {
	age: 30
}

const nameLength = user.name?.length // データ型は number | undefined

記事タイトルから若干脱線するけど、オプショナルチェーン演算子と似たような機能を持つnull合体演算子(??)との比較や使い分けについて解説された記事があったので貼っておく。

!.(非nullアサーション演算子)

!.非nullアサーション演算子と呼ぶ。

これはJavaScriptではなくTypeScriptの機能。

この演算子の機能は、nullishの可能性のあるプロパティがnullishではないこと、つまりnullでもundefinedでもないことをコンパイラーに教えてあげる(保証してあげる)こと。

名前が「非null〜」であるが、nullish(null または undefined)を指す。

先述したnull合体演算子(??)に関しても「nullじゃなかったら左辺をそのまま、nullだったら右辺を返す」ではなく「nullishだったら左辺をそのまま、nullishだったら右辺を返す」である。英語表記がNullish coalescingでしっかりとnullishと書かれている。(「nullish合体演算子」「非nullishアサーション演算子」の方が絶対分かりやすいよね)

先ほど使ったサンプルコードで名前をオプションのプロパティを持つユーザーオブジェクトの名前の文字列長さに100を足してコンソールに出力するコードを書く。

type User = {
  age: number
	name?: string
}

const user: User = {
	age: 30
}

const nameLength = user.name?.length

console.log(nameLength + 100) // Object is possibly 'undefined'.

nameLengthがundefinedの可能性を持つ以上、undefined + 100はエラーになる。

このコードではnameを持たないオブジェクトを例にしているが、nameはオプショナルなプロパティであり、操作するオブジェクトはnameを持っている条件で考える。

type User = {
	age: number
	name?: string
}

const user: User = {
	age: 30,
	name: 'サンプル太郎'
}

const nameLength = user.name.length // Object is possibly 'undefined'.

console.log(nameLength + 100)

userオブジェクトにname: 'サンプル太郎'を定義&オプショナルチェーン演算子を消したとしてもUser型の段階でnameはstring | undefinedなので、user.name.lengthが「Object is possibly ‘undefined’.」でコンパイルエラーになり、やはりuser.name?.lengthにする必要がある→出力コードはエラーになる。

もったいぶった説明になってしまったけど、ここで!.の登場。

type User = {
	age: number
	name?: string
}

const user: User = {
	age: 30,
	name: 'サンプル太郎'
}

const nameLength = user.name!.length // number

console.log(nameLength + 100) // 106

user.name!.lengthでnameプロパティがnullsihではないことを保証することでnumber型となり、コンソールの出力は成功する。

最後に

これまでReactでのフロントエンド開発をする上では?.しか使ったことがなかったけど、オプショナルなプロパティにアクセスした得られる値を後続の処理にも使いたい場合は!.を使う必要があることがわかったのと両者の違いがわかった。

余談だが、JavaScript、TypeScriptでオブジェクトやクラスのプロパティにアクセスする時の.(例:response.data)はチェーン演算子と呼ぶ。

Udemyのオススメ講座はこちら↓

TypeScript+Next.js+Laravelハンズオンはこちら↓

デスク周りのオススメアイテムはこちら↓

この記事が気に入ったら
フォローしてね!

よかったらシェアしてね!

この記事を書いた人

大学院(機械工学)→重工業→エンジニア→プロダクトマネージャー(PdM)兼エンジニア

神戸で「つながる勉強会」を運営↓
https://tsunagaru-kobe.connpass.com/

神戸グルメのインスタアカウントを運用しています。

目次
閉じる