TypeScriptのユーティリティ型を再実装する Part2

# はじめに
TypeScriptではよく行われる型変換をサポートするために、[ユーティリティ型](https://www.typescriptlang.org/docs/handbook/utility-types.html)というものを提供しています。この記事ではそんなユーティリティ型を再実装してみようと思います。全部で21ケースありますが、1ケース(`ThisType`)は単なる空のマーカインターフェイスで、4ケースはintrinsic型(TypeScriptのコンパイラに隠された型)なので残りの16ケースを紹介します。この記事では5ケース紹介します。Part1は[こちら](https://qiita.com/KokiSakano/items/65e294173688f977a291)です。Part3は[こちら](https://qiita.com/KokiSakano/items/1e78528383d2b53943b5)です。
# Omit
`Omit`は`Omit<Type, Key>`のように書きます。`Type`から`Key`をキーに含むプロパティを除いたものを返す型です。
```ts
type User = {
  id: string;
  name: string;
  mailaddress: string;
};
type OmitUser = Omit<User, 'mailaddress'>;
```
上記のように使用することができ、`OmitUser`は以下のような型を持ちます。
```
{
  id: string;
  name: string;
}
```
実装はオブジェクトを扱う時のお決まりのMapped Typesを用いて行います。
```ts
type MyOmit<Type, Keys extends keyof Type> = {
  [P in keyof Type as P extends Keys ? never : P]: T[Key]
};
```
`Type`をMapped Typesで組み直すときに、`P`が`Keys`の一部であれば`never`を返してプロパティを作成しないようにしました。
TypeScriptの実装は以下のようになっています。
```ts
type Omit<T, K extends keyof any> = Pick<T, Exclude<keyof T, K>>;
```
`Exclude`関数はすぐ下で紹介しますが、第一引数から第二引数を省くような型となっています。つまり、オブジェクトのキーから不要なキーを削除したものをオブジェクトから`Pick`するように作られています。再実装したものもTypeScriptの実装も第二引数に渡せる型以外は動作は同じです。前者はオブジェクトを構成するキーしか渡せませんでしたが、後者は`string | number | symbol`ならなんでもOKです。どうせ省くので`keyof any`とした方が汎用性は高いと考えています。
# Exclude
`Exclude`は先述の通り第一引数から第二引数を省くような型です。以下のように`Exclude<UnionType, ExcludeMembers>`のように書くことができます。
```ts
type Sample01 = Exclude<string | number | (() => void), Function>;
```
第一引数から第二引数を省くので、結果は`string | number`となります(Fuctionは関数を包含する型です)。
この型の実装は以下のように書くことが出来ます。
```ts
type MyExclude<UnionType, ExcludeMembers> = UnionType extends ExcludeMembers ? never : UnionType;
```
[Conditional Types]は`string | number extends any`のように左にユニオンを渡すと`string extends any | number extends any`のように分離する仕組みがあります。これはその仕組みを生かしたものとなっています。`UnionType`を分離して`ExcludeMembers`に含まれるものだけ`never`にして削除しています(`never`はユニオンとして扱うとEmptyになって消えます)。
TypeScriptの実装も型名以外は同じです。

# Extract
`Extract`は`Exclude`の逆で第二引数の型に含まれるものだけ残します。つまり
```ts
type Sample01 = Extract<string | number | (() => void), Function | symbol>;
```
とすると`() => void`が得られます。一致するではなく、含まれるというのがポイントです(上記の例ではsymbolではないが、`() => void`が残った)。
実装も`Exclude`の条件分岐を逆にするだけです。
```ts
type MyExtract<Type, Union> = Type extends Union ? Type : never;
```
これも、TypeScriptの実装は型名以外は同じです。
# NonNullable<Type>
`NonNullable`はnullとundefinedを引数から省く型です。
```ts
type Sample01 = NonNullable<string | number| null | undefined>;
```
だと`string | number`となります。
実装は先ほどの`Exclude`を使って
```ts
type MyNonNullable01<Type> = Exclude<Type, null | undefined>;
```
または`Exclude`を使わずに
```ts
type MyNonNullable02<Type> = Type extends null | undefined ? never : Type;
```
のように行いました。直感的ですね。
TypeScriptでは以下のようになっています。
```ts
type NonNullable<T> = T & {};
```
これで`null`と`undefined`を弾けるのは直感的ではないですね。Intersection Types`A & B`はAとBの両方の型が合わさったような型を生成します。つまり`NonNullable`は空のオブジェクト`{}`と引数を合成したような型を生成します。オブジェクトで構成されている型はこれとIntersectionをとっても型に変化はありません(あらゆるオブジェクトは`{}`を含んでいると言えるので)。しかし、オブジェクトでない型は`{}`を含んでいないし、`{}`もその型を含んでいないので、オブジェクトではない型は`{}`とIntersectionをとってもそんな型は存在しないということで`never`となってしまいます。そしてオブジェクトではない型は`null`と`undefined`しか存在しないので、この型は`null`と`undefined`を弾くことが出来ます(例えば`string`はプリミティブでオブジェクトではなさそうですが、`slice`などのプロパティを持つオブジェクトでもあるので弾かれません)。
# Parameters<Type>
`Parameters`は関数のパラメータを取得する関数です。
```ts
type SampleFunc = (arg1: string, arg2: number) => void;
type SampleParameters = Parameters<SampleFunc>;
```
のように書くことができて`[arg1: string, arg2: number]`のような結果が返ってきます。`Parameters<Function>`と出来ないことに注意してください。
```ts
interface Function {
    apply(this: Function, thisArg: any, argArray?: any): any;

    call(this: Function, thisArg: any, ...argArray: any[]): any;

    bind(this: Function, thisArg: any, ...argArray: any[]): any;

    toString(): string;

    prototype: any;
    readonly length: number;

    arguments: any;
    caller: Function;
}
```
この構造が欲しいわけではなく、`(...args: any): any`が欲しいというわけです。
実装は以下のようになります。
```ts
type Parameters<T extends (...args: any) => any> = T extends (...args: infer P) => any ? P : never;
```
TypeScriptの実装そのままですが、`(...args: any) => any>`から`T extends (...args: infer P) => any`で必要な`P`だけ取得して返すようになっています。