mosya<TC> - Last型を実装して配列の最後の要素の型を取得しようの解説
この記事はmosya<TC>の問題の一つであるLast型の解説になります。
問題
配列 T を受け取り、その最後の要素の型を返す汎用的な Last
type arr1 = ["a", "b", "c"];
type arr2 = [3, 2, 1];
type tail1 = Last<arr1>; // expected to be 'c'
type tail2 = Last<arr2>; // expected to be 1
前提知識
この問題を解くにあたって型についての以下の知識を理解しておく必要があります。
Conditional Types
を理解するinfer
を理解する- スプレッド演算子を理解する
Conditional Typesを理解する
Conditional Typesは、条件によって型を変更することができる機能です。
例えば、以下のような型が考えられます。
type Foo<T> = T extends string
? string
: number;
この型は、T
がstring
型を継承している場合はstring
型を、そうでない場合はnumber
型を返します。
このように、extends
を使って条件を指定することで、型を変更することができます。
inferを理解する
infer
は、型を推論することができる機能です。
例えば、以下のような型が考えられます。
type ArrayItem<T> =
T extends (infer R)[] ? R : never;
この型は、T
が配列の場合は、配列の中の型を返します。
以下のように使うことができます。
type Foo = ArrayItem<string[]>; // string
この場合、infer R
にはstring[]
型が当てはまるので、R
はstring
型に推論され、R
を返すのでstring
型が返されます。
このように推論される型を取得するのにinfer
は役立ちます。
スプレッド演算子を理解する
スプレッド演算子...
は、配列やオブジェクトの中身を展開する演算子です。
これを使うと配列やオブジェクトの中身を展開して、新しい配列やオブジェクトを生成することができます。
実際の値だけでなく型にもこの演算子を使うことができます。
例えば、以下のような型が考えられます。
type Foo = [1, 2, 3];
type Bar = [...Foo, 4]; // [1, 2, 3, 4]
解答例
type Last<T extends any[]> = T extends [
...any[],
infer U
]
? U
: never;
今回は、T
が配列であることが条件なので、T extends any[]
という条件を指定します。
そして、配列の中の要素を返すには推論のためのinfer
と条件のためのextends
構文を組み合わせます。
スプレッド演算子を使うことで、配列の最後以外の要素を、...
で配列として取得し、最後の要素を推論させたいので、infer
を使ってU
という名前で受け取ります。
そして、extends
を使って、条件分岐でU
を返すことで、最後の要素の型を返すことができます。
Authored by
Godai@steelydylan
Webサービスを作るのが好きなWebエンジニア。子供が産まれたことをきっかけに独立し法人化。サービス開発が大好き。
好きな言語はTypeScript。