8
5

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 3 years have passed since last update.

TypeScript: RecursiveRequired<T> - 再帰的にオプショナルを外すユーティリティ型

Last updated at Posted at 2020-06-25

この投稿では、TypeScriptの型から再帰的にオプショナルを外すユーティリティ型の実装方法を紹介します。

結論

  • 先に結論としてオプショナルを外すユーティリティ型の実装を示しておきます:
type RecursiveRequired<T> = {
  [P in keyof T]-?: RecursiveRequired<T[P]>
}

「再帰的にオプショナルを外す型」ってどういう型?

TypeScriptにはビルトインのユーティリティ型にRequired<T>というものがあります。

例えば、次のFooのようにオプショナルなプロパティを持つ型に対して、

type Foo = {
  a?: number
  b?: string
}

Requiredユーティリティ型を使うと、オプショナルなプロパティをすべてオプショナルじゃなくできます:

type RequiredFoo = Required<Foo>
//=> { a: number, b: number } 型になる

Requiredはある型から別の型を計算できるので、コードの重複が減らせて便利ですが、オプショナルを外せるのはトップレベルのプロパティだけという制約があります。どういうことかというと、次の例のようにオブジェクトがネストした構造の型の子オブジェクトや孫オブジェクトのプロパティのオプショナルは外せないのです:

type Oya = {
  ko?: {
    mago?: {
      a?: string
    }
  }
}

type RequiredOya = Required<Oya>

このRequiredOyakoプロパティだけオプショナルが外れますが、ko.magoko.mago.aはオプショナルなままです。型の計算結果は次のようになります:

type RequiredOya = {
  ko: {
    mago?: {
      a?: string
    }
  }
}

この投稿で紹介する「再帰的にオプショナルを外す型」とは、以上の課題を解決するもので、ネストした構造の型でも子オブジェクトや孫オブジェクトをたどっていって、すべてのプロパティからオプショナルを外す型になります。

RecursiveRequired<T> - 再帰的にオプショナルを外すユーティリティ型

再帰的にオプショナルを外すユーティリティ型は以下のように定義されます:

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

RecursiveRequired<T>のデモ

RecursiveRequired<T>を試してみます。

まず、再帰的な構造の型を定義します。もちろんプロパティはすべてオプショナルにします:

type Node = {
  name?: string
  parent?: Node
  children?: Array<Node>
}

本当にオプショナルになっているかコンパイルエラーを起こして確かめましょう:

// 上の型を値として定義
declare const node: Node

// stringしか受け取らない関数を定義
declare function test(value: string): void

// テスト
test(node.name)
test(node.parent.name)
test(node.parent.parent.name)
test(node.parent.parent.parent.name)
test(node.children[0].name)
test(node.children[0].children[0].name)
test(node.children[0].children[0].children[0].name)

コンパイル結果は期待通りすべてエラーになります:

CleanShot 2020-06-25 at 09.48.31@2x.png

今度はオプショナルだらけのNodeRecursiveRequired<T>に渡して、オプショナルが完璧に外れた型に対してテストしてみます。再帰的にオプショナルが外れていれば、上のテストコードはコンパイルが通るはずです。

declare const node: RecursiveRequired<Node>

コンパイル結果は、期待通りエラーが起きませんでした:

CleanShot 2020-06-25 at 09.51.06@2x.png
8
5
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
8
5

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?