Type challenge / Warmup & Easy やっていく
引っ越し元
Type challenge / Warmup & Easy やっていく
#snaka_type_challenge
しょっぱなの Pick から躓いている
一度わかっても考え方を忘れるのでメモる
Pick
type MyPick<T, K extends keyof T> = { [Key in K]: T[Key] }
まず、 MyPick<T, K extends keyof T>
で K
に渡すリテラルを T
の key に含まれるものに限定する
これで以下のテストの条件をクリアする
// @ts-expect-error
MyPick<Todo, 'title' | 'completed' | 'invalid'>,
つぎに { [Key in K]: Key extends K ? T[Key] : never }
この部分について
-
[Key in K]
でK
に渡された Union の型一つづつ取り出しKey
にセット -
T[Key]
でT
型のプロパティKey
の型を取り出す
ググって答えにたどり着いたが [Key in K]: T[Key]
の [Key in K]
の部分がぱっと出てこなかった
なるほど as
で
Property に対する getter の型を定義するみたいなこともできるのか
type Getters<Type> = {
[Property in keyof Type as `get${Capitalize<string & Property>}`]: () => Type[Property]
};
Mapped Type のイメージ
Readonly
Mappded Type 理解したら Readonly は簡単だった
type MyReadonly<T> = { readonly [Key in keyof T]: T[Key] }
Tuple to Object
なんとかできた
type TupleToObject<T extends readonly string[]> = { [Key in T[number]]: Key }
First of Array
空の配列を除外するのに Conditional type で T extends []
の判定を使う発想が出てこなかった
type First<T extends any[]> = T extends [] ? never : T[0]
Length of Tuple
type Length<T> =
T extends readonly [any, any, any, any] ? 4
: T extends readonly [any, any, any, any, any] ? 5
: never
愚直に書いてみるが tuple 意外をエラーにするのができていない
愚直にやったらテストは通った
type Length<T extends readonly any[]> =
T extends readonly [any, any, any, any] ? 4
: T extends readonly [any, any, any, any, any] ? 5
: never
スマートなやり方
type Length<Type extends readonly unknown[]> = Type['length']
Typescript Series - Length of a Tuple - DEV Community
型からメソッド呼べるのか...?
知らなかった
型からメソッド呼べるのか...?
ちがうか
長さが固定である tuple は length プロパティの型として、その tuple の長さを表す数値が割り当てられているのか
const tuple = ['a', 'b', 'c'] as const
const len = tuple.length // => len: 3 という型
tuple が可変になる ( array になる ) と結果が変わる
const tuple = ['a', 'b', 'c']
const len = tuple.length // => len: number という型
Exclude
雰囲気で解けた
type MyExclude<T, U> = T extends U ? never : T
TypeScript: Documentation - Conditional Types
Conditional Type において T
に Union Type を渡したときには、それぞれに条件を適用した結果を Union として返す ( never
は Union から取り除かれる )
Awaited
type MyAwaited<T extends Promise<unknown>> = T extends Promise<infer P> ? P : never
これだと以下が満たせない
Expect<Equal<MyAwaited<Z>, string | number>>,
Promise の入れ子か
type Z = Promise<Promise<string | number>>
愚直にこれで通ったけど、良いやりかたありそう
type MyAwaited<T extends Promise<unknown>> =
T extends Promise<infer P0>
? P0 extends Promise<infer P1>
? P1
: P0
: never
再帰的に書けば良さそうという予感はあったが、答えがなければ書けなかった
再帰で書き直した
type MyAwaited<T extends Promise<any>> =
T extends Promise<infer P>
? P extends Promise<any>
? MyAwaited<P>
: P
: never
これでも行けそうなんだけど...
type MyAwaited<T> =
T extends Promise<infer P>
? MyAwaited<P>
: T
エラーとなるべきパターンがエラーにならない
// @ts-expect-error
type error = MyAwaited<number>
だからといって型パラメタの制約として extends Promise<any>
を与えると...
type MyAwaited<T extends Promise<any>> =
T extends Promise<infer P> // <= P が制約に合わないのでエラー
? MyAwaited<P>
: T
となるので、最終的に前述のようなコードになっている
type MyAwaited<T extends Promise<any>> =
T extends Promise<infer P>
? P extends Promise<any>
? MyAwaited<P>
: P
: never
きっと、良い方法ありそう
みんなの回答例を見るとこれがベストみたい
If
これは簡単
type If<C extends boolean, T, F> = C extends true ? T : F
Concat
なんとなくで書いたら合ってたっぽい
type Concat<T extends [...any], U extends [...any]> = [...T, ...U]
もうちょっとシンプルにできた
type Concat<T extends any[], U extends any[]> = [...T,...U]
Includes
type Includes<T extends readonly any[], U> =
T extends [infer V, ...(infer W)]
? V extends U
? true
: Includes<W, U>
: T extends [infer V]
? V extends U
? true
: false
: false
ちょっと違うらしい。
より厳密なチェックが必要そう
以下のテストが失敗している
Expect<Equal<Includes<[boolean, 2, 3, 5, 6, 7], false>, false>>,
Expect<Equal<Includes<[true, 2, 3, 5, 6, 7], boolean>, false>>,
うーん、Equal<T,U>
使ってる回答あるけど...
それはちょっと違うよな
謎のUtility Type _Equal の型定義むずい
type _Equal<T, U> =
(<K>() => K extends T ? 1 : 2) extends (<K>() => K extends U ? 1 : 2)
? true
: false
type Includes<T extends readonly any[], U> =
T extends [infer F, ...infer R]
? _Equal<U, F> extends true
? true
: Includes<R, U>
: false
これで通るけど _Equal の K がどこから出てきてるのかよくわからんな
参考: 898 - Includes · Issue #29174 · type-challenges/type-challenges
この Issue Comment が発祥らしい?
Equal むずいので一旦スルー
Equal の詳しい解説あった
あとで読む
これだけ Easy レベル超えてるな
Push
これは簡単
type Push<T extends any[], U> = [...T,U]
Unshift
Push の逆にしただけ
type Unshift<T extends any[], U> = [U,...T]
Parameters
Awaited とかと比べると全然かんたん
type MyParameters<T extends (...args: any[]) => any> =
T extends (...args: infer U) => any
? U
: never
T extends
の無駄な繰り返しは削れた
type MyParameters<T> =
T extends (...args: infer U) => any
? U
: never
やっと Warmup & Easy をクリアした