TSのユーティリティ型読んでみた①

# はじめに
こんにちは、みなさん！TypeScript（TS）を使っている方も多いと思いますが、今回はTSのユーティリティ型について紹介したいと思います。これが本当に便利で、コーディングがずっと楽になるんです。

TSのユーティリティ型は、型定義を簡単にしたり、コードをシンプルにしてくれる強力なツールです。しかし、意外と使い方が知られていなかったり、どのように活用すれば良いのか分からないこともあるかもしれません。

この記事では、そんなユーティリティ型を使いこなすためのコツや具体例、内部でどんなことをやっているのかを紹介していきます。ユーティリティ型をマスターすれば、開発がもっと効率的で楽しくなること間違いなしです！

さぁ、一緒にTSのユーティリティ型の世界を探検してみましょう！



# 1.Awaited

まず、一つ目にAwaited型の定義を見てみましょう。

```typescript
/**
 * Recursively unwraps the "awaited type" of a type. Non-promise "thenables" should resolve to `never`. This emulates the behavior of `await`.
 */
type Awaited<T> = T extends null | undefined ? T : // special case for `null | undefined` when not in `--strictNullChecks` mode
    T extends object & { then(onfulfilled: infer F, ...args: infer _): any; } ? // `await` only unwraps object types with a callable `then`. Non-object types are not unwrapped
        F extends ((value: infer V, ...args: infer _) => any) ? // if the argument to `then` is callable, extracts the first argument
            Awaited<V> : // recursively unwrap the value
        never : // the argument to `then` was not callable
    T; // non-object or non-thenable
```


1. `T extends null | undefined ? T`
まず、Tがnullまたはundefinedである場合、そのままTを返します。

2. `T extends object & { then(onfulfilled: infer F, ...args: infer _): any; } ?`
次に、Tがobject型で、かつthenメソッドを持つかどうかを確認します。
onfulfilled関数の型をinferキーワードで推論し、Fとして扱います。

3. F extends ((value: infer V, ...args: infer _) => any) ?
Fが関数であり、その最初の引数の型をinferキーワードで推論し、Vとしておきます。
thenメソッドの最初の引数が関数であり、その関数が受け取る値の型Vを抽出します。

4. Awaited<V>
抽出したVに対して再帰的にAwaited型を適用し、Promiseがネストしている場合でも最終的な中身の型を取り出せます。

使い道としては

```typescript
const hogeFunc = ()=>Promise.resolve('hoge')

const hoge: Awaited<ReturnType<typeof hogeFunc>>='hoge'
```
のように使われます。
以上のように、非同期処理で使用されるPromiseの中身の型を抽出することができます。

# 2. ConstructorParameters

次に、ConstructorParameters型の定義を見てみましょう。

```typescript
type ConstructorParameters<T extends abstract new (...args: any) => any> = 
    T extends abstract new (...args: infer P) => any ? 
        P : 
        never;
```

1. `T extends abstract new (...args: infer P) => any ?`
Tが引数Pを取る抽象クラスのコンストラクタであるかを確認します。この場合、引数の型リストをinferキーワードで推論し、Pとして扱います。

2. `P : never`

もし条件が満たされる場合、引数リストPの型を返します。条件が満たされない場合は、never型を返します。

使い道としては

```typescript
abstract class Person {
    constructor(public name: string, public age: number) {}
}

type Params = ConstructorParameters<typeof Person>;
```

のように使われます。この場合、`Params`は`[string, number]`となります。

以上のように、クラスのコンストラクタで受け取る引数の型を抽出することができます。


# 3. ThisParameterType

次に、ThisParameterType型の定義を見てみましょう。

```typescript
/**
 * Extracts the type of the `this` parameter for a function type, or `unknown` if the function type has no `this` parameter.
 */
type ThisParameterType<T> = T extends (this: infer U, ...args: never) => any ? U : unknown;
```

Tが`this`パラメーターを持つ関数型であるかどうかを確認します。`(this: infer U, ...args: never) => any`という形で、`this`パラメーターの型を`infer`キーワードで推論し、Uとして扱い、その型を返却します。


```typescript

function bindContext<T extends (this: any, ...args: any[]) => any>(fn: T, context: ThisParameterType<T>): (this: void, ...args: Parameters<T>) => ReturnType<T> {
    return function(this: void, ...args: Parameters<T>): ReturnType<T> {
        return fn.apply(context, args);
    };
}

class Greeter {
    name: string;

    constructor(name: string) {
        this.name = name;
    }

    greet(this: Greeter, greeting: string): string {
        return `${greeting}, ${this.name}`;
    }
}

const greeter = new Greeter("Alice");

// 正しいコンテキストでの使用
const boundGreet = bindContext(greeter.greet, greeter);
console.log(boundGreet("Hello")); // "Hello, Alice"

// 新しいコンテキストでの再利用
const anotherGreeter = new Greeter("Bob");
const anotherBoundGreet = bindContext(greeter.greet, anotherGreeter);
console.log(anotherBoundGreet("Hi")); // "Hi, Bob"
```

上記のように、関数の`this`パラメーターの型を抽出することができます。`exampleFunction`の場合、`ThisType`は`{ a: string }`となります。これにより、関数内での`this`の型を正確に把握することができます。