toggle Engineer Blog

トグルホールディングス株式会社のエンジニアブログでは、私たちの技術的な挑戦やプロジェクトの裏側、チームの取り組みをシェアします。

type-challenges の easy 問題をスムーズに解くための基礎知識

こんにちは。トグルホールディングス、プロダクトエンジニアの原口です! トグルホールディングスエンジニアアドベントカレンダーの12日目の記事です!(元記事はこちら

最近、TypeScript の型システムやユーティリティ型への理解を深めるために、type-challenges に毎日少しずつ取り組んでいます。
今回は、その中でも初級レベル (easy) の問題をすべて解いてみました。ここでは解答の解説ではなく、「事前に知っておくとスムーズに解ける TypeScript の知識」を中心にまとめています。

type-challenges とは

type-challenges は、TypeScript の型システムを駆使して様々な型を実装する練習問題集です。問題は easy(とはいえ慣れていないと難しいものも多い)から extreme まで幅広く、一般的なユーティリティ型 (Pick、Readonly、Exclude など) の再実装から、型推論や条件型を活用しなければ解けない複雑な問題まで、様々な問題が用意されています。

これらに取り組むことで、TypeScript の型に対する理解が深まります。

抑えておくと良いポイント

Distributive Conditional Types

Exclude の問題では、条件型(T extends U ? X : Y)を用いて要素ごとに型をフィルタリングする処理が必要です。

TypeScript の条件型は、ユニオン型に対して「分配」が行われるという仕様があります。
以下は、任意の型を受け取って、その型を配列にして返す ToArray の例です。

type ToArray<Type> = Type extends any ? Type[] : never;
type StrArrOrNumArr = ToArray<string | number>; // string[] | number[]

ToArraystringnumber に対してそれぞれ適用され、
ToArray<string> | ToArray<number> となり、結果として string[] | number[] という型が得られます。

この性質を利用することで、Exclude 型などを実装できます。

infer キーワードによる型推論

ParametersAwaited などの問題では、infer キーワードを用いた一部の型抽出のテクニックが必要となります。infer は条件型の中で使用でき、推論結果を変数のように扱うことが出来ます。 以下は、infer を使用して関数の戻り値の型を抽出する例です。

type MyReturnType<T> = T extends (...args: any[]) => infer R ? R : never;

// 使用例
type Func = (a: number, b: string) => boolean;
type FuncReturnType = MyReturnType<Func>; // boolean

このように、infer を用いることで、特定の部分を抽出することが可能です。

特に Awaited の問題では、再帰的に Promise をアンラップしていく必要があり、infer と条件型を組み合わせた再帰的な型定義を理解する良い練習になりました。

配列型への理解

Tuple To ObjectLength of Tuple のような配列を操作する問題では、TypeScript における配列型の特性を知っておくとスムーズに解けます。 たとえば T['length'] で配列の長さが型レベルで取り出せる点や、スプレッド構文(...T)を使った配列同士の結合などがキーポイントになります。

以下にいくつかの例を示します。

配列の長さを取得する

type Length<T extends number[]> = T['length'];

// 使用例
type TupleLength = Length<[1, 2, 3]>; // 3

配列に要素を追加する(Push)

type Push<T extends number[], U> = [...T, U];

// 使用例
type NewTuple = Push<[1, 2], 3>; // [1, 2, 3]

これらの操作を組み合わせることで、型上での配列操作が可能になります。

PromiseLike について

Awaited の解答例では、PromiseLike という型が登場します。
PromiseLikethen メソッドを持つ「thenable」なオブジェクトを表すためのインターフェースです。 これは完全な Promise 実装ではなく、then メソッドを介して Promise と似た挙動を実現する型になります。
自分は全くなじみが無かったのですが、TypeScript の定義ファイルを参照することで理解を深めることができました。

まとめ

easy レベルの type-challenges に取り組むことで、以下のような知識が得られました。

  • Distributive Conditional Types:条件型がユニオン型に対して分配的に適用される特性
  • infer キーワード:条件型内部で型推論再帰的型展開を行うテクニック
  • 配列型の特性:T['length'] による長さ取得、スプレッド構文による型操作
  • PromiseLike の理解:Promise のようなオブジェクト型への対応

これらを事前に押さえておくと、type-challenges に取り組む際のハードルを下げることができ、実際に問題に挑戦する際にはより本質的な部分にフォーカス出来ると思います。 また、これらの知識は実務でも役立つと思いますので、興味があればぜひ挑戦してみてください!