はじめに
30代になって思うのは、TypeScriptを「なんとなく」使っている期間が長すぎたということ。
anyで逃げる、型エラーが出たらas unknownで握りつぶす、複雑な型は諦めてコピペ。正直なところ、こんな使い方をしていた時期がありました。でも、Type-Challengesに出会ってから、TypeScriptの型システムに対する理解が一気に深まったんですよ。
GitHubで47,200スター以上。TypeScriptの学習リソースとしては、かなりの人気を誇るプロジェクトです。
Type-Challengesとは
Type-Challengesは、TypeScriptの型システムを学ぶためのオンラインジャッジ付きチャレンジ集です。
一言で言うと「型で解くプログラミング問題集」という感じですね。普通のコーディング問題がアルゴリズムを書くのに対して、こちらは型だけで問題を解く。最初は「型で何を解くの?」と思うかもしれませんが、やってみると奥が深いんですよ。
プロジェクトの理念は「高品質な型はプロジェクトの保守性を向上させ、潜在的なバグを回避できる」というもの。実務で使える型スキルを身につけられるのが特徴です。
特徴・メリット
1. 難易度別に段階的に学べる
問題は5つの難易度に分かれています。
- Warm-up: 1問(まずはここから)
- Easy: 13問(基本的な型操作)
- Medium: 103問(実務レベル)
- Hard: 55問(かなり難しい)
- Extreme: 17問(型の達人向け)
個人的には、Easyを全部解けるようになった時点で、日常の開発で困ることはほぼなくなりました。Mediumまで行けたら、ライブラリの型定義を読むのも苦じゃなくなる。
2. 実務で使う型操作を網羅
Pick、Omit、Readonly、ReturnTypeなど、TypeScript標準のユーティリティ型を自分で実装する問題が多いです。
これ、意外と重要で、標準の型を「使う」だけじゃなく「作れる」ようになると、カスタムユーティリティ型を自在に書けるようになります。コスパ的に、この学習効果は高い。
3. オンラインですぐに始められる
環境構築不要で、ブラウザだけで挑戦できます。公式サイト(tsch.js.org)かTypeScript Playgroundから直接アクセス可能。
通勤時間にスマホでちょっと考える、みたいな使い方もできるのがQOL上がりますね。
4. コミュニティが活発
277人以上のコントリビューターが参加していて、新しい問題も定期的に追加されています。解答例や議論も豊富なので、詰まったときに参考にできる。
インストール方法
基本的にはオンラインで完結するので、インストールは不要です。
オンラインで挑戦する場合
- tsch.js.org にアクセス
- 好きな問題を選んで解く
- 型が正しければテストがパスする
ローカルで挑戦したい場合
git clone https://github.com/type-challenges/type-challenges.git
cd type-challenges
VSCodeなどのエディタで開けば、型チェックが効いた状態で問題に取り組めます。
TypeScript Playgroundプラグイン
TypeScript Playgroundにもプラグインとして導入できます。普段からPlaygroundを使っている人にはこちらもおすすめ。
基本的な使い方
問題の構成
各問題は以下のような構成になっています。
/*
4 - Pick
-------
組み込みの `Pick<T, K>` を使わずに実装してください。
例えば:
interface Todo {
title: string
description: string
completed: boolean
}
type TodoPreview = MyPick<Todo, 'title' | 'completed'>
*/
// ここに回答を書く
type MyPick<T, K> = any
Easy問題の例: Pick
最初に挑戦すべき問題の一つが「Pick」です。オブジェクト型から指定したキーだけを抽出する型を作ります。
// 問題: MyPickを実装せよ
type MyPick<T, K extends keyof T> = {
[P in K]: T[P]
}
// 使用例
interface Todo {
title: string
description: string
completed: boolean
}
type TodoPreview = MyPick<Todo, 'title' | 'completed'>
// { title: string; completed: boolean } になる
Mapped Typesの基本が詰まった問題ですね。keyof、extends、inを理解していれば解ける。
Medium問題の例: ReturnType
関数の戻り値の型を取得する型を自作します。
// 問題: MyReturnTypeを実装せよ
type MyReturnType<T extends (...args: any) => any> = T extends (...args: any) => infer R ? R : never
// 使用例
const fn = () => {
return { x: 1, y: 2 }
}
type Result = MyReturnType<typeof fn>
// { x: number; y: number } になる
inferキーワードの使い方がわかると、型の抽出が自在にできるようになります。これは実務でもよく使う。
Hard問題の例: Union to Intersection
ユニオン型をインターセクション型に変換する問題。これはかなり難しい。
// 問題: UnionToIntersectionを実装せよ
type UnionToIntersection<U> =
(U extends any ? (x: U) => void : never) extends (x: infer I) => void
? I
: never
// 使用例
type A = UnionToIntersection<{ a: string } | { b: number }>
// { a: string } & { b: number } になる
正直、この辺りは型パズルとして楽しむ領域ですね。実務で使う機会は少ないですが、理解しておくと型システムへの理解が一段深まります。
実践的なユースケース
1. ライブラリの型定義を理解する
ReactやVueなど、人気ライブラリの型定義は複雑です。Type-Challengesで鍛えておくと、エラーメッセージの意味がわかるようになる。
// こういう型定義を見ても怖くなくなる
type ComponentProps<T> = T extends React.ComponentType<infer P> ? P : never
2. 独自のユーティリティ型を作る
プロジェクト固有の型変換が必要になったとき、すぐに書けるようになります。
// APIレスポンスから必須フィールドだけ抽出
type RequiredFields<T> = {
[K in keyof T as undefined extends T[K] ? never : K]: T[K]
}
3. 型安全なコードを書く習慣がつく
型で問題を解く経験を積むと、「この処理、型で保証できないかな」と考える癖がつきます。結果的にバグの少ないコードが書けるようになる。
4. コードレビューでの指摘力が上がる
他人のコードを見たとき、「ここはもっと型を絞れるのでは」という視点で見られるようになります。
学習のコツ
最初は答えを見てもOK
Easyでも最初は解けないことがあります。30分考えてわからなければ、解答例を見て学ぶのも一つの手。大事なのは「なぜその解法になるのか」を理解すること。
公式ドキュメントを並行して読む
TypeScriptの公式ドキュメント、特にAdvanced Typesのセクションを読みながら進めると理解が深まります。
毎日1問でも続ける
一気にやるより、毎日少しずつ。時短で効率よく学ぶなら、1日1問を習慣にするのがおすすめ。
まとめ
Type-Challengesは、TypeScriptの型システムを深く理解するための最高の教材です。
- 難易度別に189問以上の型パズル
- オンラインで環境構築不要
- 実務で使える型スキルが身につく
- コミュニティが活発で解答例も豊富
個人的には、Easyを一通り解くだけでも、日常の開発が楽になりました。anyで逃げることがなくなり、型エラーを見ても怖くなくなる。
30代エンジニアとして思うのは、「TypeScriptの型は難しい」と避けていた時間がもったいなかったということ。Type-Challengesで遊び感覚で学べば、意外とすんなり理解できます。
まずはWarm-upの1問から始めてみてください。型パズルの沼にハマるかもしれませんが、それはそれで楽しい沼なんですよね。