mosya<TC> - ネストされた配列をフラットにする型を作ろう
この記事はmosya<TC>の問題の一つであるFlatten型の解説になります。
問題
配列 T を受け取り、その最後の要素の型を返す汎用的な Flatten<S, T>
を実装してください。
type flatten = Flatten<
[1, 2, [3, 4], [[[5]]]]
>; // [1, 2, 3, 4, 5]
前提知識
この問題を解くにあたって型についての以下の知識を理解しておく必要があります。
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 Flatten<
S extends any[],
T extends any[] = []
> = S extends [infer X, ...infer Y]
? X extends any[]
? Flatten<[...X, ...Y], T>
: Flatten<[...Y], [...T, X]>
: T;
まず、今回、Flattenに代入される方は配列が前提なので、S extends any[]
という条件を指定します。
type Flatten<S extends any[], T extends any[] = []> = ...
次に、S
が配列型の場合は、その配列の最初の要素を取得してX
に代入し、残りの要素をY
に代入します。
type Flatten<S extends any[], T extends any[] = []> = S extends [infer X, ...infer Y] ? ...
そして、X
が配列型の場合は、X
を展開してY
と結合し、X
が配列型でない場合は、Y
だけを展開して、X
を展開したT
と結合し、再びFlatten
に渡します。
このように徐々に展開済みの配列を第2引数のT
に格納していき、最終的にS
が空になったらT
を返すことで、配列を展開することができます。
type Flatten<
S extends any[],
T extends any[] = []
> = S extends [infer X, ...infer Y]
? X extends any[]
? Flatten<[...X, ...Y], T>
: Flatten<[...Y], [...T, X]>
: T;
例えば、配列、[1, 2, [3, 4], [[[5]]]]
をFlatten
に渡すと、以下のように段階的に展開されます。
Flatten<[1, 2, [3, 4], [[[5]]]]>;
// 1回目
Flatten<[[2, [3, 4], [[[5]]]]], [1]>;
// 2回目
Flatten<[[3, 4], [[[5]]]], [1, 2]>;
// 3回目
Flatten<[[4], [[[5]]]], [1, 2, 3]>;
// 4回目
Flatten<[[[[5]]]], [1, 2, 3, 4]>;
// 5回目
Flatten<[], [1, 2, 3, 4, 5]>[
// 6回目
(1, 2, 3, 4, 5)
];
Authored by
Godai@steelydylan
Webサービスを作るのが好きなWebエンジニア。子供が産まれたことをきっかけに独立し法人化。サービス開発が大好き。
好きな言語はTypeScript。