Typescript の練習として type-challenges をやった備忘録です。
内容の訂正
前回の投稿では
のようにスプレッド演算子を使って型を展開することを Spread と紹介していましたが、正しくはvariadic-tuple-typesでした。
誤った情報を掲載してしまい申し訳ありませんでした。
Hello World
Hello, World!
In Type Challenges, we use the type system itself to do the assertion.
For this challenge, you will need to change the following code to make the tests pass (no type check errors).
解答例
特に言うことはないです
Pick
Implement the built-in Pick <T, K> generic without using it.
Constructs a type by picking the set of properties K from T
解答例
組み込み型の Pick を作る問題。これを解くためには以下の 3 つの型を知っておく必要があります。
keyof
keyof はオブジェクト型のキーを リテラル または number のユニオン型として取得できます。
例えば問題の例にある Todo に使用すると以下のようになります。
Mapped Types
Mapped Types は {[P in T]:T[P]}のように扱われる型です。T の部分にはユニオン型を入れます。
P には T のユニオン型の各要素が入れられます。イメージとしては for…in を思い浮かべると理解しやすいかもしれません。
下に例を示します。
Indexed Access Types
Indexed Access Types はオブジェクト型のプロパティの型を取得できます。通常のオブジェクトをイメージすると良いと思います。
例えば問題の例にある Todo に使用すると以下のようになります。
Readonly
Implement the built-in Readonly<T> generic without using it.
Constructs a type with all properties of T set to readonly, meaning the properties of the constructed type cannot be reassigned.
解答例
組み込み型の Readonly を作る問題。これを解くためには Mapping Modifiers を知っておく必要があります。
Mapping Modifiers
Mapping Modifiers は上の Mapping Types のマップにreadonlyや?を付ける事ができます。
+または-で修飾子を追加または削除ができ、これらの接頭辞を与えない場合は+として処理されます。
下に例を示します。
Tuple to Object
Given an array, transform it into an object type and the key/value must be in the provided array.
解答例
tuple 型をオブジェクト型に変換する問題です。
これを Indexed Access Types を少し詳しく知っておく必要があります。
下に例を示します(上の引用)。
First of Array
Implement a generic First<T> that takes an Array T and returns its first element’s type.
解答例
配列の先頭の型を取得する問題です。ただし、空配列が渡された際の挙動に注意する必要があります。
この問題は、以下の 2 つの機能を知っていれば解くことができます。
上の問題を解いていれば先頭の型を取得する方法は察しがついているかと思います。ここで注意する必要があるのは空配列が与えられた場合についてです。
単純に下のようにしてしまうと空配列が与えられた場合うまく動作しません。なぜなら空配列は 0 というプロパティを持っていないからです。
そこで配列が空どうかチェックし、空なら never を、そうでなければ T[0]を返すようにします。
この条件分岐の実装には Conditional Types が必要です。これは三項演算子のように型を書く事ができます。
Length of Tuple
For given a tuple, you need create a generic Length, pick the length of the tuple
解答例
与えられた型の長さを取得する問題です。これは上で紹介した内容を知っていれば解くことができます。
(予想外にも)上で触れているように配列の長さを T[“length”]のように取得できます。
そのため、下のようにしたいところですがこれではエラーが出てしまいます。
エラーを見ると、“T はプロパティ index を持ってないよ!!”と書いてあるのでそれを持っていることを typescript に教えてあげます。
T extends {length : number}のようにしても良いですが、これでは string 型も通してしまいます。
場合によるとは思いますが、今回は配列の長さを取得する型としているので any[]のようにしたほうが無難だと思います。
Exclude
Implement the built-in Exclude<T, U>
Exclude from T those types that are assignable to U
解答例
Exclude は与えられた U に割り当てられる型を T から取り除く型です。ユニオン型から特定の型を取り除く際に使われます。
この問題を解く際に重要なことは Conditional Types を適用する際に分配されるということです。下に例を引用して示します。
そのため、T の各型について U に割り当てられるなら never を、そうでないなら T を返せば良いです。
Awaited
If we have a type which is wrapped type like Promise. How we can get a type which is inside the wrapped type?
For example: if we have Promise<ExampleType> how to get ExampleType?
解答例
※もっときれいな書き方があると思います。
Promse<T>の T を返す問題です。この問題は infer を知っていれば解くことができます。
Inferring Within Conditional Types
infer は Conditional Types で使用することができる型で下のように使います。
あとは再帰に気をつけて書くことができます。
If
Implement the util type If<C, T, F> which accepts condition C, a truthy value T, and a falsy value F. C is expected to be either true or false while T and F can be any type.
解答例
型の条件分岐を行う問題です。上で紹介した Conditional Types を使って解くことができます。
Concat
Implement the JavaScript Array.concat function in the type system. A type takes the two arguments. The output should be a new array that includes inputs in ltr order
解答例
与えられた型を Array.concat のように結合する問題です。実は Typescript でもスプレッド演算子を使う事ができます。
これを知っていればもう解答は想像できるでしょう。
Includes
Implement the JavaScript Array.includes function in the type system. A type takes the two arguments. The output should be a boolean true or false.
解けませんでした。。。 正直一番この中で難しい問題だと思います。 下の文は色々検証してみた記録です。
配列型 T の中に U が含まれているか判定する問題。単純に考えると以下のようになると思います。
しかし、これではいくつかのケースに対応できません。そこで、簡単なテストを作り実際の動作を見てみましょう。
ユニオン型については予想通りですがそれいがは少し意外ですね。ここから以下の様なことがわかります。
- boolean の実態は true | false
- object の修飾子 readonly は extends に影響しない
つまり、型が同じかどうか厳密に判定できる下のような型 Equals があればいけそうです。
やっていることは、先頭を取り出して Equals===true なら true を返しそうでなければ残りを再度 Includes に入れているだけです。
ただ、こうなるような Equals が思いつきませんでした。また時間があるときに詳しく調べて解説記事を書きます。
Push
Implement the generic version of Array.push
解答例
上の Concat と同じように解けます。
Unshift
Implement the type version of Array.unshift
解答例
上の Push と同じように解けます。
Parameters
Implement the built-in Parameters generic without using it.
解答例
関数の引数の型をタプルで取得する問題です。javascript の残余引数を知っていればすぐ思いついたのではないでしょうか。
終わりに
これを書きながら解いていたら夜が明けてしまったので、残りは明日以降解きます。
やはり、普段からこういった文章を書いていないせいで 1 つ 1 つの文を考えるのに非常に時間がかかりました。
ただ、人が見ているかもしれないと考えると情報を確かなものにしなくてはならないという義務感が生まれたので、これからも学んだことはここに書き連ねていこうと思います。