Typescript の練習として type-challenges をやった備忘録です。
前回に引き続いてやっていきます。 残り 14 問、なんとかこれで解き終えて最終回にしたいです。
LastIndexOf
Implement the type version of Array.lastIndexOf
, LastIndexOf<T, U>
takes an Array T
, any U
and returns the index of the last U
in Array T
解答例
前回やったIndexOf
と同じで実質的にEqual
を作る問題ですが、
Equal
について僕は全く理解できていないので解説は下記の素晴らしい記事にお願いしたいと思います。
- https://qiita.com/Quramy/items/b45711789605ef9f96de
- https://zenn.dev/yumemi_inc/articles/ff981be751d26c
Unique
Implement the type version of Lodash.uniq
, Unique takes an Array T
, returns the Array T
without repeated values.
解答例
ひとまずいつものように再帰用のV
を追加して最後には V を返すようにしておきます。
ここで easy 編で作成した型Includes
を思い出します。これを使うことでV
にL
が含まれていなければL
をV
に追加。そうでなければ追加しないというループを簡単に作る事ができました。
MapTypes
Implement MapTypes<T, R>
which will transform types in object T to different types defined by type R which has the following structure
解答例
沼にはまってしまい解くのに 30 分ほどかかってしまいました 😓。無心で型をいじっていたら完成したためにうまく説明できないので正しく理解してから説明を追記します。
Construct Tuple
Construct a tuple with a given length.
解答例
いつものように再帰用の引数を追加してV["length"]
がL
になるまで回します。ありがたいことにL
が 1000 を超えることはないので上限に引っかかりません。
Number Range
Sometimes we want to limit the range of numbers…
解答例
再帰用の引数を作りますが初期値に上で作成したConstructTuple
を使います。このようにすればV["length"]
をL
から始められるので、残りはループしてあげるだけです。
Combination
It’s also useful for the prop types like video controlsList
解答例
以前にPermutation
をやったときには思いつきませんでしたが、ユニオン型とExclude
を使えば簡単に作れることに気づきました。自分の TypeScript 筋 💪 の成長を感じます。
Subsequence
Given an array of unique elements, return all possible subsequences.
A subsequence is a sequence that can be derived from an array by deleting some or no elements without changing the order of the remaining elements.
解答例
ひとまず順番が重要そうなのでinfer
でT
を分けておきます。
当然これでは[1]
だけになってしまうので、R
を使って再帰させることを考えます。
現在の処理であればSubsequence
は常にタプルを返すのでこれを展開、[L]
とのユニオン型にすれば最終的にすべての[L]
になりそうです。
最後に順番を維持する必要があるので[L, ...Subsequence<R>]
という風にL
を先頭につけると…
上のような処理を経て[1, 2]
に展開されます。Subsequence<[1, 2, 3]>
の場合はあまりにも長いので割愛しますが、同じように[1,2,3]
になります。
FirstUniqueCharIndex
Given a string s, find the first non-repeating character in it and return its index. If it does not exist, return -1. (Inspired by leetcode 387)
解答例
ひとまずいつものように再帰用のタプルと infer を使ってT
の先頭 1 文字を取り出します。
ここで問題の繰り返されない文字について考えるとL
がR
の中に含まれていなければ繰り返されていないと言えそうです。つまり、
${string}${L}${string}
については、Template Literal Types
を参照してください。残りはS
に現在の Index を保持させるようにして再帰させてみます。
エラーがでてしまっており問題があるようです。冷静に処理を追うと 2 回目の再帰の際T
には"abc"
が入るので、L
が"a"
,R
が"bb"
と推測されます。
当然"a"
は"bb"
には含まれないのでこの時点でのS["length"]
===1
が返ってしまいます。
これを避けるために、S
にL
が含まれているかどうかの判定を追加し、含まれていればすぐに再帰するように変更することで上の問題を解決します。
GetMiddleElement
Get the middle element of the array by implementing a GetMiddleElement
method, represented by an array
If the length of the array is odd, return the middle element If the length of the array is even, return the middle two elements
解答例
とりあえず infer と再帰 を使ってT
の両端の要素を 1 つずつ取り除きます。1 つずつ取り除いていけば最終的にはT
の要素数は 1 か 2 になるだろうという思考です。
T
の要素数が偶数の時、最終的にC
が[ ]
と推測されてしまい思ったような動作をしないので、
再帰させる前にT
の要素数が 2 の際は特例として再帰させずにT
を返すようにします。
Integer
Please complete type Integer<T>
, type T
inherits from number
, if T
is an integer return it, otherwise return never
.
解答例
T extends number
を付けないようにしていたためにあまり美しくない感じになってしまいました。
やっている事自体は単純なので解説は割愛しますが、重要なのはnumber extends T
でT
がnumber
だったときの分岐をすることだと感じました。
ToPrimitive
Convert a property of type literal (label type) to a primitive type.
解答例
いきなり「オブジェクト型を展開してなんやかんやして…」と考えるとしんどいので、T
をプリミティブにするようなConvert<T>
を考えます。
何も考えずextends
を大量に使ってもいいのですが、流石に辛いので再帰で回すようにします。
Everyday Types(プリミティブがたくさん書いてあります)
こうしておけば仮にプリミティブな型が増えてもP
に増やすだけでよくなります。
null
やundefined
等は今回の例にないのでP
に入れてませんが増やせば問題ありません。
残りはこれをToPrimitive<T>
のT
に使うだけです。最終的に下のようになりました。
DeepMutable
Implement a generic DeepMutable which make every parameter of an object - and its sub-objects recursively - mutable.
解答例
上と同じように再帰しながらreadonly
を外していきます。
ただし() => 1
はRecord<any,any>
に割り当て可能なので先に関数の判定をする必要があり、そこでしばらく沼ってました。
All
Returns true if all elements of the list are equal to the second parameter passed in, false if there are any mismatches.
解答例
T[number]
でユニオン型を作りそれがA
に割り当て可能かチェックします。
今回のテストケースだと下のような型でもエラーはでないのですが、[1, 1, 1, never]
のような型が与えられた際に予想と異なる動作をするので、Equal
を使って厳密に判定しています。
Filter
Implement the type Filter<T, Predicate>
takes an Array T
, primitive type or union primitive type Predicate
and returns an Array include the elements of Predicate
.
解答例
いつも通り再帰用のV
を追加し、1 文字取り出してP
に割り当て可能か判定、可能ならV
にL
を追加して再帰をしています。
終わりに
これでついに medium 編完了です!!👏
色々調べてとても勉強になり、型パズル力を解く前に比べて圧倒的に強くすることができました。
ですがまだ良くわかっていないところ、例えばEqual
の動作について理解できていないので、記事を作るという名目でいつか詳しく調べたいと思います。
hard 編はこれから違う勉強をする予定なので、すぐにやることはないと思いますがどこかで解きたいなと思っています。
今回はこれで終わりです。駄文を読んでくださりありがとうございました。