【TypeScript】nullableなプロパティをすべてnon-nullableにするユーティリティ型

はじめに

こんにちは kimizuy です。

本記事では undefinednull の型注釈のあるプロパティを持つオブジェクト型から、それらの nullable な型注釈を取り除き、どの階層のプロパティもすべて non-nullable な型に変換するカスタムユーティリティ型を紹介します。

例えば、SSG のようにビルド時にページ生成する場合など、生成に必要なデータがなければランタイムエラーになる状況があります。この場合、「型は nullable だけど実態としては必須のデータ」は開発では存在チェックやオプショナルチェーンしないで non-nullable に扱いたいですよね。そういったときに、このカスタムユーティリティ型の利用が考えられます。

実際には、以下の Foo 型のように複数階層まで nullable まみれの型から undefinednull を取り除きます。

type Foo =
  | {
      a1?: string;
      b1: string | null;
      c1?: { c2: string | undefined };
    }
  | undefined;

type Foo = {
  a1: string;
  b1: string;
  c1: { c2: string };
};

tldr

TypeScript がデフォルトで持つ NonNullable ユーティリティ型を利用し、再帰型を作ります。-? は見慣れないですが、このように書くことでオプショナル修飾子 ? を取り除けます。

type DeepNonNullable<T> = NonNullable<
  {
    [P in keyof T]-?: DeepNonNullable<T[P]>;
  }
>;

以下は使用例です。 すべてのプロパティが必須になっている foo 変数は c2 プロパティが初期化されていないため型エラーになっています。

type Foo =
  | {
      a1?: string;
      b1: string | null;
      c1?: { c2: string | undefined };
    }
  | undefined;

type AllNonNullableFoo = DeepNonNullable<Foo>;

const foo: AllNonNullableFoo = { a1: "A", b1: "B", c1: {} };
// エラー
// Property 'c2' is missing in type '{}' but required in type '{ c2: string; }'.

この例は TypeScript Playground でも試せます。

補足: なんで Required<Type> じゃなくて NonNullable<Type> を使うの?

TypeScript には他にも NonNullable<Type> に似た Required<Type> というデフォルトのユーティリティ型があります。この型は明示的な nullundefined を取り除かず、オプショナル修飾子 ? だけを取り除きます。

type Required<T> = { [P in keyof T]-?: T[P]; }

(自分が混同していたので備忘録として残します)

参考

おわりに

Gatsby では GraphQL の型を生成してくれる神機能がありますが、生成された型には基本的に null の型注釈がついてきます。それを解決したくてこの型を作ってみました。プロパティがすべて必須になるためリスクを意識しつつ使っていきたいですね。

以上、お読みいただきありがとうございました。

Gaji-Laboでは、Jamstackが得意なフロントエンドエンジニアを募集しています

弊社ではJamstackの知見で事業作りに貢献したいフロントエンドエンジニアを募集しています。大きな制作会社や事業会社とはひと味もふた味も違うGaji-Laboを味わいに来ませんか?

もちろん、一緒にお仕事をしてくださるパートナーさんも随時募集中です。まずはお気軽に声をかけてください。お仕事お問い合わせや採用への応募、共に大歓迎です!

求人応募してみる!

投稿者 Yamasaki Kimizu

React, Redux, TypeScript プロジェクトでフロントエンド領域を担当。個人でも Next.js アプリの開発をしています。日課はRSSで取得した技術記事を読むこと、最近の関心は Core Web Vitals です。将来はでかい犬が飼いたいです。