はじめに
型パズルをやった備忘録を残しまする
環境整え
これ入れた。
あとはフォルダ指定して、やるだけ。
解いた
4-Pick (Easy)
問題
組み込みの型ユーティリティPick<T, K>
を使用せず、T
からK
のプロパティを抽出する型を実装します。
例えば:
interface Todo { title: string description: string completed: boolean } type TodoPreview = MyPick<Todo, 'title' | 'completed'> const todo: TodoPreview = { title: 'Clean room', completed: false, }
思考
ふむふむ。どうやるんやろ。
なんか、&
使えそうだけど、全然わからん。
ブルベリ本見てたら使えそうなやつあった。それがmapped types。これ使ってみるか。
そして、T型から型を取得すればいい。
あとは型引数の部分型を指定してやる。
↓こうなった。
type MyPick<T extends object, K extends keyof T> = { [KKey in K]: T[KKey] }
んでこれは正解っぽい。
7-Readonly (Easy)
問題
組み込みの型ユーティリティReadonly<T>
を使用せず、T
のすべてのプロパティを読み取り専用にする型を実装します。実装された型のプロパティは再割り当てできません。
例えば:
interface Todo { title: string description: string } const todo: MyReadonly<Todo> = { title: "Hey", description: "foobar" } todo.title = "Hello" // Error: cannot reassign a readonly property todo.description = "barFoo" // Error: cannot reassign a readonly property
思考
さっきの問題と似てるくね?またmapped types使う。
そして、あとはreadonlyつけるだけかな。
readonlyはkey名の前につけるの注意。
type MyReadonly<T> = { readonly [TKey in keyof T]: T[TKey] }
11-Tuple to Object (Easy)
問題
タプルを受け取り、その各値のkey/valueを持つオブジェクトの型に変換する型を実装します。
例えば:
const tuple = ['tesla', 'model 3', 'model X', 'model Y'] as const type result = TupleToObject<typeof tuple> // expected { 'tesla': 'tesla', 'model 3': 'model 3', 'model X': 'model X', 'model Y': 'model Y'}
思考
さっきから同じ感じじゃない? これもmapped typesよな。
そしてーーー、mapped typeのkey部分({ [P in K]: T }
のK部分)にはユニオン型が必要。
なので、タプル→ユニオンに変換できれば良い。
調べたら、Tuple[number]
ってやれば良さそう。
type TupleToObject<T extends readonly (string | number | symbol)[]> = { [TKey in T[number]]: TKey }
11-Tuple to Object (Easy)
問題
配列T
を受け取り、その最初のプロパティの型を返すFirst<T>
を実装します。
例えば:
type arr1 = ['a', 'b', 'c'] type arr2 = [3, 2, 1] type head1 = First<arr1> // expected to be 'a' type head2 = First<arr2> // expected to be 3
思考
ほう。これはーーーtypeofの出番か? でもなさそう。普通にこの記述してみたけど、中身が入ってない時はneverを返せって言われる。
type First<T extends any[]> = T[0]
じゃあconditional types使って中身がない時はnever返しますか?(半ギレ)明示的に。 あーーだとしても型引数の配列の長さを知りたいな。lengthプロパティの取り方を調べて。
type First<T extends any[]> = T["length"] extends 0 ? never : T[0]
なんかneverのパターンをconditional types 使うのがちょっと綺麗じゃないけどいいんかな。
解答見たけどconditional types使ってたな。てか T extends []
っていう使い方おもろすぎ。あと可変長タプル型使うやつ。
18-Length of Tuple (Easy)
問題
タプルT
を受け取り、そのタプルの長さを返す型Length<T>
を実装します。
例えば:
type tesla = ['tesla', 'model 3', 'model X', 'model Y'] type spaceX = ['FALCON 9', 'FALCON HEAVY', 'DRAGON', 'STARSHIP', 'HUMAN SPACEFLIGHT'] type teslaLength = Length<tesla> // expected 4 type spaceXLength = Length<spaceX> // expected 5
思考
だからさっきやったって。 ↓で終わり。lengthプロパティを呼び出す。
type Length<T extends readonly any[]> = T['length']
43-Exclude (Easy)
問題
組み込みの型ユーティリティExclude <T, U>
を使用せず、U
に割り当て可能な型をT
から除外する型を実装します。
例えば:
type Result = MyExclude<'a' | 'b' | 'c', 'a'> // 'b' | 'c'
思考
たぶん、mapped types とconditional typesを組み合わせるんかな?いや違うか。
最終的にユニオン型で返さないといけないよな、ユニオン型の引き算ってなんやねん。まぁそれがExcludeの本質なんだけど。
含むかどうかはextendsとかでわかるけど。
...すまん、わからんかった。
(答えを見る時間)
そんな使い方できるのーーー!conditional types。
記事みてみた。
ほえーー覚えとこう。
189-Awaited (Easy)
問題
Promise ライクな型が内包する型をどのように取得すればよいでしょうか。
例えば:Promise<ExampleType>
という型がある場合、どのようにして ExampleType を取得すればよいでしょうか。
type ExampleType = Promise<string> type Result = MyAwaited<ExampleType> // string
思考
これだめだ、たぶんガチで知らないやつ。試行錯誤が無駄なやつ、だって知らないとわからない問題ぽいから。
だから答え見るわ
(答えを見る時間)
へーーーー!!! infer
便利やなぁ。これあれば、<>に囲まれた型を取得できるなぁ。
あと PromiseLike
。Promiseのオブジェクトの条件がthenが入っていること。だけなのは衝撃。
268-If (Easy)
問題
条件値C
、 C
が truthy である場合の戻り値の型T
、C
が falsy である場合の戻り値の型F
を受け取るIf
を実装します。
条件値C
はtrue
かfalse
のどちらかであることが期待されますが、T
と F
は任意の型をとることができます。
例えば:
type A = If<true, 'a', 'b'>; // expected to be 'a' type B = If<false, 'a', 'b'>; // expected to be 'b'
思考
肌感、めっちゃ余裕じゃね?conditional types使えば良さそう。
type If<C extends boolean, T, F> = C extends true ? T : F
え、解けたw
533-Concat (Easy)
問題
JavaScript のArray.concat
関数を型システムに実装します。この型は 2 つの引数を受け取り、受け取ったイテレータの要素を順に含む新しい配列を返します。
例えば:
type Result = Concat<[1], [2]>; // expected to be [1, 2]
思考
んーー?すげぇ実装っぽいけどこれ型システムだけでできるんだ?? まぁあれか、タプル型を返すからいいのか、あくまで型だもんな。
当て勘で↓を書いてみたら当たった。ほぼ実装やん。
type Concat<T extends readonly any[], U extends readonly any[]> = [...T, ...U]
898-Includes (Easy)
問題
JavaScriptのArray.include
関数を型システムに実装します。この型は、2 つの引数を受け取り、true
やfalse
を出力しなければなりません。
例えば:
type isPillarMen = Includes<['Kars', 'Esidisi', 'Wamuu', 'Santana'], 'Dio'> // expected to be `false`
思考
うーん、condditional typesなのはそうじゃないか。 あとは、タプル型でもらったのをユニオン型にして部分型か判断?
んでこうなった↓ だけどこれ不正解ね
type Includes<T extends readonly any[], U> = U extends T[number] ? true : false;
わかんねーー正解を見る。
(正解見てる)
めっちゃなるほど。infer使うのか。いやーーーinferいいな。 inferとconditional typesの合わせ技めっちゃ便利やん。
3057-Push (Easy)
問題
Array.push
のジェネリックバージョンを実装します。
例えば:
type Result = Push<[1, 2], '3'> // [1, 2, '3']
思考
なんかーーinferでいけそうだな。 やってみるか、、いけた。
type Push<T extends any[], U> = T extends [...infer Rest] ? [...Rest, U] : never
んーーけど、infer使いたいがためにconditional typesやっているだけで、neverは定義しなくていいんだよな。 答えも見てみるか。
www そりゃそうだわwww
type Push<T extends unknown[], U> = [...T, U]
3060-Unshift (Easy)
問題
Array.unshift
の型バージョンを実装します。
例えば:
type Result = Unshift<[1, 2], 0> // [0, 1, 2,]
思考
...なめすぎでは? ほぼ前問と同じ。
type Unshift<T extends unknown[], U> = [U, ...T]
3312-Parameters (Easy)
問題
組み込みの型ユーティリティParameters<T>
を使用せず、T
からタプル型を構築する型を実装します。
例えば:
const foo = (arg1: string, arg2: number): void => {} type FunctionParamsType = MyParameters<typeof foo> // [arg1: string, arg2: number]
思考
これもinferでいけそう。 やってみるか、、、いけた。なんなんinfer
type MyParameters<T extends (...args: any[]) => any> = T extends (...args: infer Args) => any ? [...Args] : false
解答見てみたら、そもそも展開する必要なかったわ。 そうなのか、タプル型になるのか、inferは。
おわりに
よしーーーEasyモードは終わり。 次はMedium編。