投稿日:2021年5月8日
Pickは型の一部を柔軟に抜き出して、新しい型を作ることのできるユーティリティ関数です。この記事ではPick を実際のコードを踏まえて詳しく解説します。
Pick<Type, Keys>は型の一部を柔軟に抜き出して、新しい型を作ることのできるユーティリティ関数です。この記事ではPick<Type, Keys>を実際のコードを踏まえて詳しく解説します。
この記事は以下の環境でテストされました。
Pick<Type, Keys>はジェネリックパラメータの第1引数Typeには抜き出す元となるタイプ、第2引数Keysには抜き出すキーの一致条件を渡します。
// 抜き出す対象の型を定義
interface Todo {
title: string;
description: string;
completed: boolean;
}
/**
* Todoから一部を抜き出す
*/
type TodoPreview = Pick<Todo, "title" | "completed">;
ここでKeysは"title"または"completed"という値になっています。よくある間違いとして["title", "completed"]の様な配列を渡してしまわない様に注意しましょう。
内部を見るとわかるのですが、KeysはKeys extends keyof Tを満たす必要があります。
上のTodoPreviewは以下のような記述をしたときと同じ型になります。
type TodoPreview = {
title: string;
completed: string;
}
ここでは試しにPick<Type, Keys>をつかって他のユーティリティ関数Omit<Type, Keys>を実装してみます。
OmitはPick<Type, Keys>と逆をイメージするといいでしょう。ジェネリックパラメータの第1引数Typeには抜き出す元となるタイプ、第2引数Keysには排除するキーの一致条件を渡します。
export type Omit<T, K extends keyof T> = T extends any
? Pick<T, Exclude<keyof T, K>>
: never;
まずはOmitのジェネリックパラメータの第2引数を見てみましょう。Kに渡す条件として、K extends keyof Tという条件を追加しています。この様に書くことでKの値をTでい指定した型のキーのみに制限することができます。
次に右辺の型の定義を見てみましょう。
この定義では三項演算子が使われ、TがT extends anyを満たす場合にはPick<T, Exclude<keyof T, K>>、それ以外の場合にはneverが定義される様になっています。しかしneverは型として割り当てることができないはずです。つまりTがanyを満たさない場合にはneverによるエラーが発生するため、Tがunknownにならないことを強制させることができます。
Tが正しい条件を満たしている場合にはPick<T, Exclude<keyof T, K>>の定義が渡されます。この様に型のキーの条件はExclude<Type, Keys>を使うことで排他的な表現を扱うことができます。