TypeScript: レコードの Optional なプロパティの型のみ(あるいはその逆)を抽出する

TypeScript: レコードの Optional なプロパティの型のみ(あるいはその逆)を抽出する

February 22, 2022

こんな型があるとします。

type Person = {
  name: string;
  age: number;
  isAdult: boolean;
  siblings: string[];
  birthplace?: string;
  height?: number;
  hasPartner?: boolean;
  friends?: string[];
};

こうすれば Optional なプロパティのみを抽出できます。

type OptionalPropertyKeys<T> = {
  [K in keyof T]-?: undefined extends T[K] ? K : never;
}[keyof T];

type OptionalPersonPropertyKeys = OptionalPropertyKeys<Person>;
// 👉 "birthplace" | "height" | "hasPartner" | "friends"

type OptionalPersonProperty = Pick<Person, OptionalPersonPropertyKeys>;
// 👉 {
//      birthplace?: string;
//      height?: number;
//      hasPartner?: boolean;
//      friends?: string[];
//    }

/**
 * ということでこうして使いましょう。
 */
type OptionalProperty<T> = Pick<
  T,
  {
    [K in keyof T]: undefined extends T[K] ? K : never;
  }[keyof T]
>;

逆に、Required なプロパティのみを抽出するならばこうです。

type RequiredPropertyKeys<T> = {
  [K in keyof T]-?: undefined extends T[K] ? never : K;
}[keyof T];

type RequiredPersonPropertyKeys = RequiredPropertyKeys<Person>;
// 👉 "name" | "age" | "isAdult" | "siblings"

type RequiredPersonProperty = Pick<Person, RequiredPersonPropertyKeys>;
// 👉 {
//      name: string;
//      age: number;
//      isAdult: boolean;
//      siblings: string[];
//    }

/**
 * ということでこうして使いましょう。
 */
type RequiredProperty<T> = Pick<
  T,
  {
    [K in keyof T]: undefined extends T[K] ? never : K;
  }[keyof T]
>;

あるいは任意の型で抽出したいならば、ジェネリックをもう1つ貰うようにすれば良いです。

type PickedTypeProperty<T, U> = Pick<
  T,
  {
    [K in keyof T]: T[K] extends U ? K : never;
  }[keyof T]
>;

type StringPersonProperty = PickedTypeProperty<Person, string>;
// 👉 { name: string; }

type StringOrNumberPersonProperty = PickedTypeProperty<Person, string | number>;
// 👉 { name: string; age: number; }