zukucode
主にWEB関連の情報を技術メモとして発信しています。

TypeScript オプショナルチェーンとNull合体演算子の使い方

APIから受け取るデータには、値が存在しない項目が含まれることがあります。

TypeScriptでは、オプショナルチェーンの?.とNull合体演算子の??を使用すると、nullundefinedを安全に扱えます。

これらは、欠けてもよい値を簡潔に扱うための演算子です。本来必須の値が欠けている問題を隠すために使うものではありません。また、空文字や0を有効な値として残したい場合は、すべてのfalsy値を置き換える||ではなく??を選びます。

値がない可能性を型で表す

以下は、利用者と住所を表す型です。

type Address = {
  city: string;
  postalCode?: string;
};

type User = {
  name: string;
  address?: Address;
};

プロパティ名の後ろに?を付けると、そのプロパティが存在しない可能性を表せます。

user.address.cityと直接書くと、addressundefinedかもしれないためTypeScriptでエラーになります。

オプショナルチェーンで参照する

const city = user.address?.city;

addressが存在すればcityを返し、存在しなければundefinedを返します。

複数の階層へ続けて使用することもできます。

const length = user.address?.postalCode?.length;

この場合、addressまたはpostalCodeが存在しなければlengthundefinedです。

Null合体演算子で初期値を設定する

undefinedの代わりに表示用の文字列を使用したい場合は、??を組み合わせます。

const city = user.address?.city ?? '未設定';
const postalCode = user.address?.postalCode ?? '郵便番号なし';

左側がnullまたはundefinedの場合だけ、右側の値を使用します。

||との違い

||は、空文字、数値の0falseも値がないものとして扱います。

const count = 0;

console.log(count || 10); // 10
console.log(count ?? 10); // 0

件数の0や設定値のfalseを有効な値として扱いたい場合は、??を使用します。

type Settings = {
  pageSize?: number;
  showHelp?: boolean;
};

const pageSize = settings.pageSize ?? 20;
const showHelp = settings.showHelp ?? true;

pageSize0なら0showHelpfalseならfalseをそのまま使用します。

配列の要素を安全に取得する

配列の検索結果にも使用できます。

type Product = {
  productId: string;
  name: string;
};

const products: Product[] = [
  { productId: 'p1', name: 'ノート' },
];

const productName = products.find((item) => item.productId === 'p2')?.name
  ?? '商品が見つかりません';

findは対象が見つからない場合にundefinedを返します。

?.nameで安全に名前を取得し、見つからなければ??の右側を使用します。

関数を安全に呼び出す

関数自体が存在しない可能性がある場合にも?.を使用できます。

type Props = {
  onComplete?: () => void;
};

const save = (props: Props) => {
  // 保存処理
  props.onComplete?.();
};

onCompleteが渡されている場合だけ関数を実行します。

値を代入する用途には使えない

オプショナルチェーンは値の参照に使用します。左辺へ値を代入することはできません。

エラーになる例
user.address?.city = '東京';

値を変更する場合は、先に存在を確認します。

if (user.address) {
  user.address.city = '東京';
}

?.を追加するとエラーは消せますが、本来必須のデータが欠けている問題を見逃す場合があります。

値がなくてもよい項目だけをoptionalにし、必須項目がない場合は入力チェックやAPIレスポンスの検証でエラーにします。

?.は途中の値がない場合にundefinedを返し、??nullまたはundefinedの場合の初期値を指定します。この2つを使い分けることで、値がない場合の処理を簡潔に書けます。


関連記事

  • TypeScript unknown型を安全に確認して使用する

    unknownは、どのような値が入っているか分からないことを表す型です。外部から受け取ったJSONやcatchした値を、確認せず操作したくない場合に使います。anyは型チェックを無効にしますが、unk...


  • TypeScript typeとinterfaceの基本的な使い分け

    TypeScriptでは、typeとinterfaceのどちらでもオブジェクトの形を定義できます。似ている部分が多いため、最初はどちらを使えばよいか迷うことがあります。今回は基本的な書き方と、分かりや...


  • TypeScript Problem DetailsをZodで検証してAPIエラーを扱う

    fetchは、サーバーが400や500を返してもPromiseをrejectしません。そのため、HTTPステータスを確認し、レスポンスの形式を検証してから、画面で扱いやすいエラーへ変換する必要がありま...


  • TypeScript mapとfilterで配列を変換する

    mapとfilterは、配列から新しい配列を作るメソッドです。どちらも元の配列を変更しませんが、役割は異なります。要素数を保ったまま各値を変換するならmap、条件に合う要素だけへ減らすならfilter...


  • AjvでAPIのレスポンスの型チェックを行う

    TypeScript fetchの共通処理を実装するでfetchの共通処理を作成しましたが、これはAPIからのレスポンスの形式があっている前提で処理を行っています。例えば以下はAPIのレスポンスの型は...


  • React useEffectで無限ループが発生するときに確認すること

    ReactのuseEffectを利用したときに無限ループが発生してしまうことがあります。特に注意したいのが、ESLintのreact-hooks/exhaustive-depsで表示された警告をUpd...


  • React 関数コンポーネントにプロパティを設定する

    React+TypeScriptで作成した関数コンポーネントにプロパティを設定する方法を紹介します。以下は送信ボタンのコンポーネントを作成しています。isSubmittingというプロパティを用意して...


  • React react-router-domの基本的な使い方

    React+TypeScriptのプロジェクトにreact-router-domを導入してSPAのアプリケーションを作成する方法を紹介します。まずはreact-router-domのパッケージをインス...


  • react-router-dom ページごとにファイルを分割する

    react-router-domでシングルアプリケーションを作成していると、ページ数が増えるにしたがって作成されるjsファイルのファイルサイズもどんどん増えていきます。ページごとにファイルを分割し、そ...


  • react-router-domでプログラムから画面遷移を行う

    react-router-domでプログラムから画面遷移を行う方法を紹介します。history.psuh(遷移先URL)の形式で実行すると、画面遷移を行えます。historyはreact-router...