🕌

type-fest で便利なユーティリティ型を使う

2024/05/25に公開
2

type-fest とは

TypeScript の便利なユーティリティ型を集めたライブラリです。
https://github.com/sindresorhus/type-fest

ユーティリティ型とは

既存の型を変形して新しい型を作成するためのTypeScriptの機能です。
TypeScript にもデフォルトで Partial, Required, Readonly などいくつかのユーティリティ型が用意されています。
https://www.typescriptlang.org/docs/handbook/utility-types.html

なぜ type-fest があると嬉しいのか

TypeScript の wiki に新しいユーティリティ型を標準ライブラリに追加しない方針が説明されています。
名前の衝突や型定義の不一致、広く使用された後の変更の難しさを回避するためです。
既存のユーティリティ型のように必要性がないものは追加されません。

そのため、ユーザーは自分のニーズに合わせて独自のユーティリティ型を定義することが推奨されています。この独自のユーティリティ型を type-fest は定義してくれています。

使用例

数多くのユーティリティ型の中から、便利そうな型をいくつかピックアップしてみました。

PartialDeep

ネストされたオブジェクトの全てのプロパティをオプショナルにします。

import { PartialDeep } from 'type-fest';

interface User {
  name: string;
  address: {
    city: string;
    zip: string;
  };
}

const user: PartialDeep<User> = {
  address: {
    city: 'New York'
  }
};

Merge

二つのオブジェクト型をマージします。

import { Merge } from 'type-fest';

type Foo = {
  a: string;
  b: number;
};

type Bar = {
  b: boolean;
  c: number;
};

type FooBar = Merge<Foo, Bar>;
// Resulting type: { a: string; b: boolean; c: number; }

SetOptional

指定したプロパティをオプショナルにします。

import { SetOptional } from 'type-fest';

interface User {
  id: number;
  name: string;
  age: number;
}

type UserWithOptionalName = SetOptional<User, 'name'>;
// Resulting type UserWithOptionalName = { id: number; age: number; name?: string | undefined;}

SetRequired

指定したプロパティを必須にします。

import { SetRequired } from 'type-fest';

interface User {
  id: number;
  name?: string;
  age?: number;
}

type UserWithRequiredName = SetRequired<User, 'name'>;
// Resulting type: { id: number; name: string; age?: number; }

AsyncReturnType

非同期関数の戻り値の型を取得します。

import { AsyncReturnType } from 'type-fest';

async function fetchData() {
  return { data: 'some data' };
}

type FetchDataReturnType = AsyncReturnType<typeof fetchData>;
// Resulting type: { data: string; }

ReadonlyDeep

オブジェクトの全てのプロパティを再帰的に読み取り専用にします。

import { ReadonlyDeep } from 'type-fest';

interface User {
  name: string;
  address: {
    city: string;
    zip: string;
  };
}

const user: ReadonlyDeep<User> = {
  name: 'John',
  address: {
    city: 'New York',
    zip: '10001'
  }
};

// user.address.city = 'Los Angeles'; // Error: Cannot assign to 'city' because it is a read-only property.

Discussion

nap5nap5

便利そうな型をいくつかピックアップしてみました

Exceptも結構ハンディかとおもいます

Omitはデフォだと、補完しないですが、ExceptOmit対象の補完が効くようになります

定義側

import { Except } from "type-fest";

export type Omit<ObjectType, KeysType extends keyof ObjectType> = Except<
  ObjectType,
  KeysType
>;

使用側

import { IsEqual } from "type-fest";
import { Omit } from "./utils";

type User = {
  id: string;
  name: string;
  tel: string;
};

type ExceptedUser = Omit<User, "tel">;

type Judge = IsEqual<
  ExceptedUser,
  {
    id: string;
    name: string;
  }
>;

demo code.

https://codesandbox.io/p/sandbox/eager-tdd-8jvpk6?file=%2Fsrc%2Findex.ts