🎯
【type-challengesに挑戦!】Pick の解答と解説
はじめに
この記事は、type-challenges の問題を解説するシリーズです。今回は Pick の問題に挑戦します。
問題
TypeScript の組み込みユーティリティ型 Pick<T, K> を使用せずに実装します。
Pick<T, K> は、型 T から指定したプロパティ K のみを選択して新しい型を構築します。
例:
interface Todo {
title: string
description: string
completed: boolean
}
type TodoPreview = MyPick<Todo, "title" | "completed">
const todo: TodoPreview = {
title: "Clean room",
completed: false
}
解答
type MyPick<T, K extends keyof T> = {
[P in K]: T[P];
};
解説
ステップ 1: 型パラメータの定義
type MyPick<T, K extends keyof T>
2 つの型パラメータを定義しています:
-
T: 元となる型(例:Todoインターフェース) -
K: 抽出したいプロパティ名の型
ステップ 2: K extends keyof T の制約
K extends keyof T
この部分が非常に重要です:
-
keyof T: 型Tのすべてのプロパティ名をユニオン型として取得- 例:
keyof Todoは"title" | "description" | "completed"
- 例:
-
K extends keyof T:KがTのプロパティ名であることを制約- これにより、存在しないプロパティを指定するとコンパイルエラーになります
型安全性の例:
// ✅ OK: "title" は Todo のプロパティ
type ValidPick = MyPick<Todo, "title">;
// ❌ エラー: "invalid" は Todo のプロパティではない
type InvalidPick = MyPick<Todo, "invalid">;
// Type '"invalid"' does not satisfy the constraint '"title" | "description" | "completed"'.
ステップ 3: マップド型 [P in K]: T[P]
{
[P in K]: T[P];
}
この構文はマップド型と呼ばれ、以下の処理を行います:
-
[P in K]:Kに含まれる各プロパティ名をPとして反復処理 -
T[P]: 元の型Tから、プロパティPの型を取得(インデックスアクセス型)
具体例で見てみましょう:
type TodoTitle = MyPick<Todo, "title">;
この場合、以下のように展開されます:
-
Kは"title" -
P in "title"でPが"title"になる -
T[P]つまりTodo["title"]でstring型が取得される - 結果:
{ title: string }
複数のプロパティを指定した場合:
type TodoPreview = MyPick<Todo, "title" | "completed">;
展開プロセス:
-
Kは"title" | "completed" -
P in "title" | "completed"でPが順に"title","completed"になる - それぞれの型を取得:
-
Todo["title"]→string -
Todo["completed"]→boolean
-
- 結果:
{ title: string; completed: boolean; }
まとめ
この記事では、type-challenges の Pick 問題を通じて、以下の TypeScript の重要な概念を学びました:
- ジェネリック型パラメータ: 再利用可能な型定義の作成
-
keyof演算子: 型のプロパティ名をユニオン型として取得 -
型制約 (
extends): 型パラメータに制限を設けて型安全性を確保 - マップド型: 既存の型からプロパティを反復的に変換
-
インデックスアクセス型 (
T[P]): 型からプロパティの型を取得
参考リンク
- https://github.com/type-challenges/type-challenges
- https://github.com/type-challenges/type-challenges/blob/main/questions/00004-easy-pick/README.md
- https://www.typescriptlang.org/docs/handbook/utility-types.html
- https://www.typescriptlang.org/docs/handbook/2/mapped-types.html
- https://www.typescriptlang.org/docs/handbook/2/keyof-types.html
- https://www.typescriptlang.org/docs/handbook/2/indexed-access-types.html
Discussion