15
Help us understand the problem. What are the problem?

posted at

updated at

Organization

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

概要

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

中級も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

Register as a new user and use Qiita more conveniently

  1. You can follow users and tags
  2. you can stock useful information
  3. You can make editorial suggestions for articles
What you can do with signing up
15
Help us understand the problem. What are the problem?