TypeScriptの勉強のために type-challenges の初級を【全問解答】する

## 概要

TypeScript をまともに触ったことが無いので、使いながら覚えようということで type-challenges の 初級 を**全問解答**してみました。(2022/09/21現在)
と、格好をつけていますが、13問しかないのですぐです。

https://github.com/type-challenges/type-challenges

中級も8割ぐらい挑戦してみて、ある程度こういうケースはこういうふうに対応するんだという、ヒントとなるコツが見えてきたので、それもまとめてみます。


## 解答のヒント

### 配列を分割する
``` .ts
// T が any[] の場合
T extends [...infer A, infer B] ? A : never

// 上記の例では、T = [1,2,3,4] であれば、 A には [1,2,3], B には 4 が入る
// T が [] の場合、 never が返る
```

### 文字列を分解する
``` .ts
// T の最初の文字がLに入り、残りがRに入る
T extends `${infer L}${infer R}`

// T が xxxxhoge である場合、 X に xxxx が入る
T extends `${infer X}hoge`
```

### 配列をユニオン型にする
``` .ts
T[number]

// 使用例
type TupleToUnion<T extends unknown[]> = T[number]
TupleToUnion<[123, '456', true]> //=> 123 | '456' | true 
```

### ユニオン型を用いてオブジェクト型を作る(mapped types)

``` .ts 
// K は文字列だと 1つのプロパティを持つオブジェクト型になるが、
// K がユニオン型だと、各型をプロパティとして持ったオブジェクト型になる
// 右辺(valueに該当する箇所) は P 以外でももちろん良い
// プロパティのキーは string | number | symbol 型である必要がある
// 下の例で言えば、 K extends string | number | symbol である必要がある
{ [P in K]: P }


// 特定のプロパティのみ残したい場合
// KからPを作り、そのPについて、Xを満たすかどうかで分岐をかける。
// never が返ってきた場合、生成されるオブジェクト型からそのプロパティは排除される
{ [P in K as P extends X ? P : never]: P }
```

### 配列の要素数を出す

数を条件に分岐をさせる場合にはこれをよく利用する。
`A['length'] extends 5 ? true : false` ←こういう使い方を再起処理で使ったりする。

``` .ts
type A = [1,2,3]
A['length'] // => 3

//　ちなみに、string型に対して、同じ容量で文字数を出すことはできない
type S = "string"
S['length'] // => number 
```

### 全く同じ型であるかどうかを確認する

``` .ts
Equal<A, B> extends true ? 真の場合 : 偽の場合

// A が false, B が boolean の場合に
// A extends B だと true になる
// このケースで false にしたい場合は Equal<A, B> を使う
```


## 以下解答

``` 4 - Pick.ts
// K の型を縛る点がミソ
type MyPick<T, K extends keyof T> = {
  [P in K]: T[P]
}

// 以下は間違い。 
// K の型が定まっていないので、以下の点で明確ではなくなるので型エラーがでる
// 1. Kがプロパティキーに入れられる型かどうか、
// 2. Kから取り出したプロパティPがTのキーとして利用できる型かどうか
// type MyPick<T, K> = {
//  [P in K]: T[P]
// }
```

``` 7 - Readonly.ts
// readonly を各プロパティに付けているだけ
type MyReadonly<T> = {
  readonly [P in keyof T]: T[P]
}
```

``` 11 - Tuple to Object.ts
// 配列をユニオン型に変換するテクニック、 T[number] を使えば良い
// PropertyKey は、プロパティーキーに入れることができる型のユニオン型 string | number | symbol と同義
type TupleToObject<T extends readonly PropertyKey[]> = {
  [P in T[number]]: P
}
```

``` 14 - First of Array.ts
// 配列の最初 or 最後を取り出す時に infer を使うやり方 Array extends [infer F, ...infer B]
// T extends Tの条件 ? 合致する場合の型 : 合致しない場合の型
type First<T extends any[]> = T extends [infer A, ...infer B] ? A : never

// 以下は T が [undefined] の場合に never が返ってしまう
// type First<T extends any[]> = T[0] extends undefined ? never : T[0]

// 一番簡単なやり方
type First<T extends any[]> = T extends [] ? never : T[0]
```

``` 18 - Length of Tuple.ts
// 配列から要素数を抜き出すやり方 T['length']
// 問題で渡される配列は as const がついているので、 型引数の中に readonly が必要
type Length<T extends readonly unknown[]> = T['length']
```

``` 43 - Exclude.ts
// ユニオン型から特定の条件に合致する別のユニオン型を作る
// U と合致した型 T のみを使ったユニオン型を作っている
type MyExclude<T, U> = T extends U ? never : T
```

``` 189 - Awaited.ts
// MyAwaited<T extends Promise<unknown>> で Promise型だけ引数にとるようにします
// T extends Promise<infer A> で 引数の型をAとして取り出します
// A extends Promise<unknown> で、Aが Promise型かどうかで分岐をかけています
type MyAwaited<T extends Promise<unknown>> = T extends Promise<infer A>
  ? A extends Promise<unknown>
    ? MyAwaited<A>
    : A
  : never
```

``` 268 - If.ts
// extends を用いた三項演算子の基礎のような問題
type If<C extends boolean, T, F> = C extends true ? T : F
```

``` 533 - Concat.ts
type Concat<T extends unknown[], U extends unknown[]> = [...T, ...U]
```

``` 898 - Includes.ts
type Includes<T extends readonly any[], U> = T extends [infer A, ...infer B]
  // ここが A extends U, もしくは U extends A だと 
  // [A, U]に [boolean, true] または [true, boolean] などの組み合わせがある場合に
  // true 扱いになってしまうので、 Equal<>を使う
  ? Equal<A, U> extends true
    ? true
    : Includes<B, U>
  : false
```

``` 3057 - Push.ts
type Push<T extends unknown[], U> = [...T, U]
```

``` 3060 - Unshift.ts
type Unshift<T extends unknown[], U> = [U, ...T]
```

``` 3312 - Parameters.ts
//  (...args: any[]) => any と Function は 同義
// 関数型の引数の型を infer で A に移し、それをそのまま返している
// T は関数型なので、T extends (...args: infer A) => unknown が合致しないケースは無いので、
// 三項演算子の不一致時の戻り値は never
type MyParameters<T extends Function> = T extends (...args: infer A) => unknown
  ? A : never
```