TypeScriptのユーティリティ型メモ
TypeScript使ってるときにたまに使うイディオムまとめ。複雑な型定義を作るときにしか使わないものもあるし、ライブラリ化するほどのものでもないのでメモ書きとして(思い出したらちょっとずつ更新していく)
オブジェクト操作系
MyPick<T, K extends keyof any>
型TのプロパティKのみを抽出した型を作る。標準のPickはK extends keyof T
の制約がかかっているのでTに存在しないプロパティ名を指定するとエラーになる。たまに困ることがあるのでメモ。いきなり名前が適当で申し訳ない。
type MyPick<T, K extends keyof any> = Pick<T, Extract<keyof T, K>>;
PartiallyPartial<T, K extends keyof T>
型Tのうち、Kに指定したプロパティのみnullableにする。
type PartiallyPartial<T, K extends keyof T> = Omit<T, K> & Partial<Pick<T, K>>;
標準で提供されているPartial
は全部のプロパティをnullableにしてしまうので「それはやりすぎ」ってパターンは結構多くて使う。
TypeScriptバージョン3.5から標準でOmit
が提供されるようになったのでPick<T, Exclude<keyof T, K>>
とか書かなくて良くなった。
上記の実装だとプロパティKが型Tに存在しないときにエラーになってしまうので、存在しないキーを無視したい場合は次のような実装にする。
type PartiallyPartial<T, K extends keyof any> = Omit<T, K> & Partial<Pick<T, Extract<keyof T, K>>>;
Partialの中身は前述のMyPickと同じ。
Prop<T, K extends keyof any>
型TのプロパティKの型を返す。型TにプロパティKが存在しないときはneverを返す。
type Prop<T, K extends keyof any> = K extends keyof T ? T[K] : never;
基本的にT['propname']
で事足りる。
Merge<X, Y>
2つのオブジェクトを合成した型を作る。同名のプロパティが存在する場合は型Yが優先。
type Merge<X, Y> = Omit<X, keyof Y> & Y;
普通の交差型は各プロパティの型も交差型になる({ gender: number } & { gender: string }
なら{ gender: number & string }
になるし{ gender: number } & { gender: 'male' | 'female' | 'other' }
なら{ gender: never }
)これだと不都合なことが多いので。
配列型操作系
配列型を操作するための型関数。TypeScriptではタプルも配列型として扱うのでタプル操作に使ったりもする。
ArrayType<A extends unknown[]>
配列の型を返す。
type ArrayType<A extends unknown[]> = A[number];
型定義作るほどでもないけど、配列型に[number]
渡すと型が取得できるという機能を覚えておくためにメモ。
Prepend<E, A extends unknown[]>
配列型の先頭要素に型Eを追加する。配列型を操作するときに関数の引数として取り回すイディオムは覚えておくとたま~に使える。
type Prepend<E, A extends unknown[]> =
((arg: E, ...args: A) => void) extends ((...args: infer R) => void)
? R
: never
;
Head
配列型の先頭要素の型を返す。型Tが配列型ではないときはその型を返す。
type Head<T> = T extends unknown[] ? T[0] : T;
配列型以外を受け付けないような実装にすることもある。
type Head<T extends unknown[]> = T[0];
Tail
配列型の先頭を取り除いた型を返す。
type Tail<T extends unknown[]> =
((...args: T) => void) extends ((arg: unknown, ...args: infer R) => void)
? R
: never
;