🧩

【TypeScript】Object型から任意の型のフィールドのみを抽出する

2021/03/20に公開

開発中になるほどなと思ったので備忘録として&Zennデビューしたかったので投稿します.

やりたいこと

例えば,以下の Person 型から, number 型のフィールドのみを取り出したい.

type Person = {
    name : string;
    kanaName: string;
    age: number;
    height: number;
}

任意の型のフィールドのみを抽出する PickByType

以下の PickByType を使うことで任意の型のフィールドのみを抽出することができる.

Playground

type PickByType<T extends Record<string, unknown>, PickedType = unknown> = Pick<
  T,
  {
    [K in keyof T]: T[K] extends PickedType ? K : never;
  }[keyof T]
>;

type NumericPersonFields = PickByType<Person, number>;
/*
type NumericPersonFields = {
    age: number;
    height: number;
}
*/

仕組み

Pick<Type, Keys>Type から Keys で指定したフィールドを抽出する組み込み型である.

PickByType 中の Pick[K in keyof T]: T[K] extends PickedType ? K : never; は 各 フィールドが PickedType を満たしていればそのフィールドの key (K) を返し,満たしていなければ neverを返す.

したがって, type NumericPersonFields = PickByType<Person, number>; のときは以下のような値をとる.

// `type NumericPersonFields = PickByType<Person, number>;` のときの
// { [K in keyof T]: T[K] extends PickedType ? K : never; } の結果
{
    name : never;
    kanaName: never;
    age: "age";
    height: "height";
}

上記の結果にさらに [keyof T] を加えることで, "age" | "height" という Union型になる.
[keyof T] を取ることで, never 型 の key が排除されるところがポイントである.

このような仕組みで, type NumericPersonFields = PickByType<Person, number>;
type NumericPersonFields = Pick<Person, "age" | "height">; と処理されて, 指定した型のみのフィールドを抽出できる.

Discussion