Featured image of post Typescript Selfmade Utility Type

Typescript Selfmade Utility Type

TypeScriptには公式が提供してくれている Pick<T, Keys>Extract<T, U> などのUtility型があるが、それ以外にも自作のUtility型を作ることができる。

Utility型とは、型から別の型を生成する型のこと。

次のようなJSONデータを取り扱い、type によって条件分岐を書きたいとする。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
[
  {
    "type": "create",
    "name": "Tokyo"
  },
  {
    "type": "update",
    "id": "qzwswsdedrf"
  },
  {
    "type": "delete",
    "id": "plokiujyh"
  }
]

このJSONデータをTypeScriptで型定義すると次のようになる。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
type InputAction = {
  type: 'create';
  name: string;
} | {
  type: 'update';
  id: string;
} | {
  type: 'delete';
  id: string;
};

Unionを使って、 type プロパティの値によって異なる型を持つ InputAction 型を定義している。

この InputAction 型から type プロパティの値のみを抽出してUnion型を作るUtility型は以下のようになる。

1
type ExtractActionType<T> = T extends { type: infer U } ? U : never;

使ってみる。

1
type ActionType = ExtractActionType<InputAction>; // 'create' | 'update' | 'delete'

解説。

ExtractActionType<T> はジェネリクスとして T 型を受け取る。

型定義における条件分岐であるConditional Typesを用いる。ただの三項演算子。

T 型が { type: infer U } の構造をもつ場合、 type プロパティの型 U を抽出して返す。そうでない場合は never 型を返す。

infer キーワードは、条件型の中で型を推論し、その型を後で使用するために一時的に保持するために使用される。ここでは文字列リテラル型(createupdate)を推論している。

ExtractActionType<T> を使って type がとりうる文字列リテラルのUnion型を抽出することで、Switch文で type プロパティの値によって処理を分岐させる時に型安全性を保つことができる。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
switch (action.type) {
  case 'create':
    return action.name;
  case 'update':
    return action.id;
  case 'delete':
    return action.id;
  default: {
    const _: never = action.type;
    return _;
  }
}

このSwitch文では default ブロックで never 型を返すことで、全てのケースを網羅していることをTypeScriptに伝えることができる。

自作のUtility型を作ることで、 InputAction 型を土台に堅牢な実装をすることができた。


参考

Built with Hugo
Theme Stack designed by Jimmy