TypeScriptのMapped Typesは、既存の型から新しい型を動的に生成する強力な機能です。この記事では、Mapped Typesの基本から実践的な使用方法まで、詳しく解説していきます。

Mapped Typesとは

Mapped Typesは、既存のオブジェクト型のプロパティを変換して、新しい型を作成する機能です。これにより、型の再利用性が高まり、コードの重複を減らすことができます。

基本的な構文

type MappedType<T> = {
  [P in keyof T]: T[P]
}

実践的な使用例

1. すべてのプロパティをオプショナルにする

type Optional<T> = {
  [P in keyof T]?: T[P]
}

interface User {
  name: string;
  age: number;
}

type OptionalUser = Optional<User>;
// 結果: { name?: string; age?: number; }

2. すべてのプロパティを読み取り専用にする

type Readonly<T> = {
  readonly [P in keyof T]: T[P]
}

interface Todo {
  title: string;
  completed: boolean;
}

type ReadonlyTodo = Readonly<Todo>;
// 結果: { readonly title: string; readonly completed: boolean; }

3. プロパティの型を変換する

type ToString<T> = {
  [P in keyof T]: string
}

interface Person {
  id: number;
  age: number;
  active: boolean;
}

type StringPerson = ToString<Person>;
// 結果: { id: string; age: string; active: string; }

高度な使用例

1. 条件付きMapped Types

type ConditionalType<T> = {
  [P in keyof T]: T[P] extends string ? string : number
}

interface Mixed {
  name: string;
  count: number;
  id: string;
}

type Converted = ConditionalType<Mixed>;
// 結果: { name: string; count: number; id: string; }

2. Mapped Typesとジェネリックの組み合わせ

type Record<K extends keyof any, T> = {
  [P in K]: T
}

type Fruit = Record<'apple' | 'banana' | 'orange', number>;
// 結果: { apple: number; banana: number; orange: number; }

ベストプラクティス

1. 型の命名: 明確で説明的な名前を使用する

2. コメント: 複雑なMapped Typesには適切なコメントを付ける

3. 再利用: 共通のパターンは独立した型として定義する

よくある間違いと解決策

1. キーの制約を忘れる

// 悪い例
type Unconstrained<T> = {
  [P in T]: string  // エラー: Tは制約されていない
}

// 良い例
type Constrained<T extends string | number | symbol> = {
  [P in T]: string
}

2. ネストされたプロパティの処理

// 再帰的なMapped Types
type DeepReadonly<T> = {
  readonly [P in keyof T]: T[P] extends object ? DeepReadonly<T[P]> : T[P]
}

まとめ

Mapped Typesは、TypeScriptの型システムを最大限に活用するための重要な機能です。適切に使用することで、型安全性を保ちながら、柔軟で再利用可能な型定義を作成できます。

実際のプロジェクトでは、以下の点に注意して使用しましょう:

  • 型の可読性を維持する
  • 必要以上に複雑な型を作成しない
  • ドキュメントとコメントを適切に追加する

これらの原則に従うことで、メンテナンス性の高い型定義を実現できます。