🦍

【TypeScript】Utility Types ハックして自作の型を作ってみた

2023/02/20に公開

Utility Types とは

https://www.typescriptlang.org/docs/handbook/utility-types.html

type User = {
  name: string;
  age: number;
  address: string;
  phoneNumber: string;
};

type PartialUser = Partial<User>;

// PartialUserの型
PartialUser = {
  name?: string;
  age?: number;
  address?: string;
  phoneNumber?: string;
};

上記の例のように、Utility Typesとはコード内で型変換を簡単にしてくれます。
ただ、phoneNumberのみオプショナルにしたいとき、それに相当するUtility Typesは存在しないので、組み合わせて作ってみます。

自作 UtilityTypes を作る

PartialPickOmitを組み合わせて作ります。

type customPartial<T, K extends keyof T> = Omit<T, K> & Partial<Pick<T, K>>;

実際の使い方は以下のようになります。

type User = {
  name: string;
  age: number;
  address: string;
  phoneNumber: string;
};

type PartialUser = customPartial<User, 'phoneNumber'>;

// PartialUserの型
PartialUser = {
  name: string;
  age: number;
  address: string;
  phoneNumber?: string;
};

// phoneNumberはオプショナルなのでなくても良い
const user: PartialUser = {
  name: 'hoge',
  age: 20,
  address: 'fuga',
};

解説

type customPartial<T, K extends keyof T>

の部分から見ていきます。
T は User が入ります。keyof TnameageaddressphoneNumberの 4 つの文字列の Union 型になります。つまり、KnameageaddressphoneNumberのいずれかの文字列になります。

次に、Omitの部分です。

Omit<T, K>;

Omit はTからKを除外した型を返します。
例えば

type User = {
  name: string;
  age: number;
  address: string;
  phoneNumber: string;
};

type OmitUser = Omit<User, 'name' | 'age'>;

のようにすると、OmitUserの型は

OmitUser = {
  address: string;
  phoneNumber: string;
}

となります。

次に、PartialPickの部分です。

Partial<Pick<T, K>>;

PartialTの全てのプロパティをオプショナルにします。PickTからKを選択した型を返します。
そして選択された型をPartialでオプショナルにします。

例えば

type User = {
  name: string;
  age: number;
  address: string;
  phoneNumber: string;
};

type PickAndPartialUser = Partial<Pick<User, 'name' | 'age'>>;

のようにすると、PartialUserの型は

type PickAndPartialUser = {
  name?: string;
  age?: number;
};

となります。

最後にこれらを組み合わせると、

type PartialUser = {
  name?: string;
  age?: number;
  address: string;
  phoneNumber: string;
};

となります。

これにて utilitytypes のハック完了です。

オプショナルをとるパターン

逆に必須にしるパターンも作ってみます。

type customRequired<T, K extends keyof T> = Omit<T, K> & Required<Pick<T, K>>;

同じ考えでできますね。

GitHubで編集を提案
Gohan DAO テックブログ

Discussion