かゆい所に手が届く TypeScript の 型ユーティリティ・ライブラリ 【ts-toolbelt の紹介】

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1425145/1b811bb3-8232-f07b-6896-55f251851da1.png)
※ 画像は [公式リポジトリ](https://github.com/millsp/ts-toolbelt) より引用

# はじめに

[ts-toolbelt](https://github.com/millsp/ts-toolbelt) を簡単に紹介する記事です。TypeScript が提供する Partial などの [Utility Types](https://www.typescriptlang.org/docs/handbook/utility-types.html) を強化し、より豊富なラインナップにしたイメージのライブラリです。ts-toolbelt があれば、これまで自作していた型ユーティリティが不要になるかもしれません。

# 概要

- GitHub Star数: 2.4k（2021/5/22時点）
- 最もテストされた最大の型ライブラリ[^README/About]
- 200以上のユーティリティをもつ[^README/About]
- lodash や [ramda](https://ramdajs.com/) にあるような関数を型レベルで実装[^README/About]
- 型を計算、変更、作成する簡単な方法を提供することを使命としている[^README/About]

Star数は TypeScript の 型ユーティリティ・ライブラリ としては一強状態だと思います。[^他にもあれば]


[^README/About]: [README/About](https://github.com/millsp/ts-toolbelt#about) より
[^他にもあれば]: 他にもあればコメントて教えていただければ幸いです。

# 利用例

筆者が個人的に使用したことのあるユーティリティの一部を紹介します。

## オブジェクトの一部のプロパティのみ省略可能にする

標準ユーティリティの Partial は 全てのプロパティを省略可能にしますが、部分的に省略可能にすることができません。ts-toolbelt の `Object.Optional` を使えば部分的省略が可能です。

```typescript
import { Object } from 'ts-toolbelt';

type User = {
  name: string
  age: number
};
type optional = Object.Optional<User, 'age'>;
// { name: string; age?: number | undefined; }
```
`Object.Compulsory` を使えば、一部の null 又は undefined なプロパティを必須プロパティにできます。
標準ユーティリティにある機能なら、「一部だけ～したい」という要望はだいたい実現可能と思います。

## オブジェクトの一部のプロパティを上書きする

```typescript
import { Object } from "ts-toolbelt";

type User = {
  name: string;
  age: number;
};
type stringAge = Object.Overwrite<User, { age: string }>;
// { name: string; age: string; }
```

## ネストされたオブジェクトの特定のプロパティの型を取得する

```typescript
import { Object } from "ts-toolbelt";

type Nested = {
  three: {
    two: {
      one: "Go!";
    };
  };
};
type letus = Object.Path<Nested, ['three', 'two', 'one']>;
// type letus = "Go!"
```

## オブジェクトの値の Union を取得する

```typescript
import { Object } from "ts-toolbelt";

const EraChar2Kanji = {
  R: '令和',
  H: '平成',
  S: '昭和'
} as const
type eraKanji = Object.UnionOf<typeof EraChar2Kanji>;
// type eraKanji = "令和" | "平成" | "昭和"
```

## オブジェクトを反転させる

```typescript
import { Object } from "ts-toolbelt";

const EraChar2Kanji = {
  R: '令和',
  H: '平成',
  S: '昭和'
} as const
type EraKanji2Char = Object.Invert<typeof EraChar2Kanji>;
/* type EraKanji2Char = {
  令和: "R";
  平成: "H";
  昭和: "S";
}*/
```

## Union から特定の型を選択する

```typescript
import { Union } from "ts-toolbelt";

type EventInfo =
  | {
      reason: "clear";
    }
  | { reason: "delete"; id: string };

type selected = Union.Select<EventInfo, { id: string }>;

type reason = selected["reason"];
// type reason = "delete"
```

## Union の差分を取得する

```typescript
import { Union } from "ts-toolbelt";

type abc = "a" | "b" | "c";
type adc = "a" | "d" | "c";

type diff = Union.Diff<abc, adc>;
// type diff = "b" | "d"
```

## 型レベルのテストをする
```typescript
import { Union, Test } from "ts-toolbelt";

type abc = "a" | "b" | "c";
type adc = "a" | "d" | "c";
type diff = Union.Diff<abc, adc>;

Test.checks([Test.check<diff, "b" | "d", Test.Pass>()]);
// diff は "b" | "d" であることを確認

// Test.checks は型テスト用であるため、実行されるファイルに記載しないようにします。
// どうしても実行時されるファイルに書きたい場合は Test.checks を無名関数等の中に書けばエラーにはなりません。
// 例: () => Test.checks([Test.check<diff, "b" | "d", Test.Pass>()]);
```
`Test` の使い方については、[README の Running tests](https://github.com/millsp/ts-toolbelt#running-tests) を参照ください。

## その他

他にも、型レベルの足し算やオブジェクトのマージなども可能です。
より豊富なサンプルについては、こちらの [READMEからリンクされているデモ（CodeSandbox）](https://codesandbox.io/s/ts-toolbelt-x4jly?file=/src/index.ts) を参照ください。