なにこれ
type-challengesの解答メモ
多少凝った型でもググりながら書いているのでなんとかなるでしょう。と思っていたのですが、全然わからん。。。
これを超えたらきっと理解度増してるだろうと信じて解いていきます。
easy
初級なんて簡単やろw
そう思っていたのですが洗礼を受けました。
気持ちを改めてせめて初級くらいは全部解きます。
- Pick
- Readonly
- Tuple to Object
- First of Array
- Length of Tuple
- Exclude
- Awaited
- If
- Concat
- Includes
- Push
- Unshift
- Parameters
Pick
組み込みの型ユーティリティ
Pick<T, K>を使用せず、TからKのプロパティを抽出する型を実装します。
- やること
- Tに含まれるキーのみをKで受け取れるようにする
- Kを型のキーとする
- TからKに対応する型を持ってくる
type MyPick<T, K> = anyまずはTに含まれるキーのみをKで受け取れるようにします。
type MyPick<T, K extends keyof T> = anykeyof TでT型のキーを全て列挙します。extendsでKの条件を指定します。K extends keyof TでKはT型のキーのみを受け付けるようになります。
次にKをMyPick型のキーに指定します。
type MyPick<T, K extends keyof T> = {
[N in K]: any
}[N in K]でKの内容をforループのように展開します。Mapped Typesという機構です。[N in K]: anyでKを展開してキーにして、その型はany型であると定義しています。
最後にTからKに対応する型を持ってきます。
type MyPick<T, K extends keyof T> = {
[N in K]: T[N]
}T[N]でT型のNに対応するキーの型を取得します。Lookup Typesという機構です。
これで完了です。
Readonly
組み込みの型ユーティリティ
Readonly<T>を使用せず、Tのすべてのプロパティを読み取り専用にする型を実装します。実装された型のプロパティは再割り当てできません。
- やること
- Tのキーと型をそのまま展開する
- readonlyを修飾させる
type MyReadonly<T> = anyまずはT型をそのまま展開します。
type MyReadonly<T> = {
[K in keyof T]: T[K]
}keyof,Mapped Types,Lookup Typesを利用してT型をそのまま展開します。
次にreadonlyを修飾させます。
type MyReadonly<T> = {
readonly [K in keyof T]: T[K]
}readonlyを指定するだけです。
これで完了です。
Tuple to Object
タプルを受け取り、その各値のkey/valueを持つオブジェクトの型に変換する型を実装します。
- やること
- タプルの値をキーにセットする
- 型はキーと同じ文字列リテラルをセットする
- (問題がそうなっているので)stringだけで構成されているarrayの場合だけ利用できるようにする
type TupleToObject<T extends readonly any[]> = anyまずはタプルの値をキーにセットします。
type TupleToObject<T extends readonly any[]> = {
[K in T[number]]: any
}T[number]でTに入ってくるタプルのindexに対応した値をとってくる。- 詳しくはこれを読むのが良さそうです。
次に型はキーと同じ文字が入るようにします。
type TupleToObject<T extends readonly any[]> = {
[K in T[number]]: K
}- 文字列リテラルで、キーと型は同じ文字列となります。
最後に問題としてnumber[]の場合はエラーにしろとテストに書いてあるのでそうします。
type TupleToObject<T extends readonly string[] = {
[K in T[number]]: K
}以上です。
First of Array
配列
Tを受け取り、その最初のプロパティの型を返すFirst<T>を実装します。
- やること
- Tの最初の値を取得する
- 配列が空の場合はneverを返す
type First<T extends any[]> = anyまずはTの最初の要素を取得します。
type First<T extends any[]> = T[0]- 単純にindexerを利用して配列の最初の値を取得します
その後、配列がからの場合はneverを返します
type First<T extends any[]> = T extends [] ? never : T[0]T extends 条件 ? :でTの内容によって返却するものを変えるように三項演算子を使います。- Tが空配列の場合はneverを返す
- Tが空配列ではない場合はT[0]を返す
以上です。
Length of Tuple
タプル
Tを受け取り、そのタプルの長さを返す型Length<T>を実装します。
- やること
- Tの長さを取得する
- タプル以外は受け付けられないようにする
type Length<T extends any> = anyまずは長さを取得します。
type Length<T extends any> = T['length']- タプルについてはドキュメントを参照するとlengthを保持しているとのことですので、これで長さを取得できます。
次にタプルのみを受け付けられるようにします。
type Length<T extends readonly any[]> = T['length']T extends readonly any[]で、タプルのみを受け付けられるようにします。- 問題で、
const t = ['a', 'b'] as constとタプルを作成しており、この場合の型を読むとreadonlyが修飾しています。 - ドキュメントにこの挙動についての説明があります。
- 問題で、
以上です。
Exclude
組み込みの型ユーティリティ
Exclude <T、U>を使用せず、Uに割り当て可能な型をTから除外する型を実装します。
- やること
- Tを全て型として展開する
- Uで受け取った値は型のキーからは除外する
type MyExclude<T, U> = anyこれは今まで回答してきたことを使えばできるのでサクッと行いましょう。
type MyExclude<T, U> = T extends U ? never : TT extends UでUを含む場合かどうかで条件分岐させます。neverは戻り値がないことを明示できます。
以上です。
Awaited
Promise ライクな型が内包する型をどのように取得すればよいでしょうか。 例えば、
Promise<ExampleType>という型がある場合、どのようにして ExampleType を取得すればよいでしょうか。
- やること
inferを使って、Promiseの中の型を取得して条件分岐する- Promise<Promise<string | number>>のようにPromiseが二重に囲まれている場合に対応する
type MyAwaited = anyPromise<string>のstringを取得するにはどうすれば良いのか?
inferを以下のように利用するとstringを取得できます。
type MyAwaited<T> = T extends Promise<(infer R)> ? R : never次に問題ではPromise<Promise<string | number>>とPromiseを二重で囲っているテストケースが存在するので、それに対応します。
type MyAwaited<T> = T extends Promise<infer R>
? R extends Promise<infer U> ? U : R
: never- extendsと三項演算子で二重のPromiseにまで対応しました。
最後に、MyAwaitedにPromiseが入ってこない場合にエラーにさせます。
type MyAwaited<T extends Promise<any>> = T extends Promise<infer R>
? R extends Promise<infer U> ? U : R
: unknown- ジェネリクスで
T extends Promise<any>の条件を追加してPromiseの何かが入ってこなければエラーにさせます。
以上です。
If
条件値
C、Cが truthy である場合の戻り値の型T、Cが falsy である場合の戻り値の型Fを受け取るIfを実装します。 条件値Cはtrueかfalseのどちらかであることが期待されますが、TとFは任意の型をとることができます。
- やること
- Cがbooleanだけを受け付けるようにする
- CがtrueだったらTを返して、それ以外はFを返すようにする
type If<C, T, F> = anyCはbooleanだけを受け付けられるようにします。
type If<C extends boolean, T, F> = any次にCを判定してTかFを返却するようにします。
type If<C extends boolean, T, F> = C extends true ? T : F以上です。
Concat
JavaScript の
Array.concat関数を型システムに実装します。この型は 2 つの引数を受け取り、受け取ったイテレータの要素を順に含む新しい配列を返します。
- やること
- TとUは配列のみを受け取れるようにする
- TとUを全て展開した配列を作成する
type Concat<T, U> = anyT, Uを配列のみ受け取れるようにします。
type Concat<T extends unknown[], U extends unknown[]> = any- any[]でもunknown[]でも良いのかなと思います。
次にT, Uの値を全て展開します。
type Concat<T extends unknown[], U extends unknown[]> = [...T, ...U]- スプレッド構文でT,Uをそれぞれ展開します。展開した配列を返却すればConcatと同等の値を返却できます。
以上です。
Includes
Implement the JavaScript
Array.includesfunction in the type system. A type takes the two arguments. The output should be a booleantrueorfalse.
(日本語化されてませんでした)
…全くわからん…
Push
Implement the generic version of
Array.push
- やること
- Tを全て展開した後にUを追加する
- TはArrayのみを受け取れるようにする
type Push<T, U> = anyスプレッド構文を使えば終わりですね。
type Push<T extends unknown[], U> = [...T, U]以上です。
Unshift
Implement the generic version of
Array.unshift
- やること
- Uをセットした後にTを全て展開する
- TはArrayのみを受け取れるようにする
type Unshift<T, U> = anyスプレッド構文を使えば終わりですね。
type Unshift<T extends unknown[], U> = [U, ...T]以上です。
Parameters
Implement the built-in Parameters
generic without using it.
- やること
- …argsで受け取ったany[]型を抽出して返却する
type MyParameters<T extends (...args: any[]) => any> = any型を抽出するためにinferを使います。
type MyParameters<T extends (...args: any[]) => any> =
T extends (...args: infer U) => any
? U
: never最後のneverは条件に入らないと思われるのですが、三項演算子で書くのでfalseの場合に何かを返すために記載しています。
以上です。
終わりに
とりあえず初級をやってみて、TypeScriptなにも分かってなかったんだと気付かされました。。。
しっかり取り組んだら、結構実力着くんじゃないかと思います。
これチームでやったら面白いんじゃないかなーと感じますね。
Includesだけissueを見ても全然わからなかったので、別途取り組もうと思います。