mcpField
T
typescript-advanced-types

typescript-advanced-types

github.com/wshobson開発41.9k94.3k 回インストール2 週間前に更新
リポジトリ

typescript-advanced-types について

typescript-advanced-types は、TypeScript の高度な型システムを扱う開発者向けのリファレンススキルです。再利用可能な型パターンや実例に加え、型テストとよくある落とし穴も扱います。

主な機能

  1. 01ジェネリクスと制約
  2. 02条件型とマップ型
  3. 03テンプレートリテラル型
  4. 04組み込みユーティリティ型
  5. 05型テストとベストプラクティス

活用シーン

  1. 01型安全な API クライアントの設計
  2. 02再利用可能なジェネリックコンポーネントの構築
  3. 03型付きフォーム検証の作成
  4. 04JavaScript から TypeScript への移行

インストール

コマンドをコピーしてターミナルで実行します。

$npx skills add https://github.com/wshobson/agents --skill typescript-advanced-types

インストール後、Claude Code で/typescript-advanced-typesを入力して読み込まれたか確認します。

同梱ファイルreferences/details.md

詳細

TypeScript の高度な型

ジェネリクス、条件型、マップ型、テンプレートリテラル型、ユーティリティ型など、TypeScript の高度な型システムを習得し、堅牢で型安全なアプリケーションを構築するための包括的なガイダンスです。

このスキルを使う場面

  • 型安全なライブラリやフレームワークを構築する場合
  • 再利用可能なジェネリックコンポーネントを作成する場合
  • 複雑な型推論ロジックを実装する場合
  • 型安全な API クライアントを設計する場合
  • フォームバリデーションシステムを構築する場合
  • 強い型付けの設定オブジェクトを作成する場合
  • 型安全な状態管理を実装する場合
  • JavaScript のコードベースを TypeScript に移行する場合

中核となる概念

1. ジェネリクス

目的: 型安全性を維持しながら、再利用可能で型に柔軟なコンポーネントを作成します。

基本的なジェネリック関数:

function identity<T>(value: T): T {
  return value;
}

const num = identity<number>(42); // Type: number
const str = identity<string>("hello"); // Type: string
const auto = identity(true); // Type inferred: boolean

ジェネリック制約:

interface HasLength {
  length: number;
}

function logLength<T extends HasLength>(item: T): T {
  console.log(item.length);
  return item;
}

logLength("hello"); // OK: string has length
logLength([1, 2, 3]); // OK: array has length
logLength({ length: 10 }); // OK: object has length
// logLength(42);             // Error: number has no length

複数の型パラメーター:

function merge<T, U>(obj1: T, obj2: U): T & U {
  return { ...obj1, ...obj2 };
}

const merged = merge({ name: "John" }, { age: 30 });
// Type: { name: string } & { age: number }
2. 条件型

目的: 条件に依存する型を作成し、高度な型ロジックを可能にします。

基本的な条件型:

type IsString<T> = T extends string ? true : false;

type A = IsString<string>; // true
type B = IsString<number>; // false

戻り値の型を抽出する:

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

function getUser() {
  return { id: 1, name: "John" };
}

type User = ReturnType<typeof getUser>;
// Type: { id: number; name: string; }

分配条件型:

type ToArray<T> = T extends any ? T[] : never;

type StrOrNumArray = ToArray<string | number>;
// Type: string[] | number[]

ネストした条件:

type TypeName<T> = T extends string
  ? "string"
  : T extends number
    ? "number"
    : T extends boolean
      ? "boolean"
      : T extends undefined
        ? "undefined"
        : T extends Function
          ? "function"
          : "object";

type T1 = TypeName<string>; // "string"
type T2 = TypeName<() => void>; // "function"
3. マップ型

目的: 既存の型のプロパティを反復処理して変換します。

基本的なマップ型:

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

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

type ReadonlyUser = Readonly<User>;
// Type: { readonly id: number; readonly name: string; }

オプショナルプロパティ:

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

type PartialUser = Partial<User>;
// Type: { id?: number; name?: string; }

キーの再マッピング:

type Getters<T> = {
  [K in keyof T as `get${Capitalize<string & K>}`]: () => T[K];
};

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

type PersonGetters = Getters<Person>;
// Type: { getName: () => string; getAge: () => number; }

プロパティのフィルタリング:

type PickByType<T, U> = {
  [K in keyof T as T[K] extends U ? K : never]: T[K];
};

interface Mixed {
  id: number;
  name: string;
  age: number;
  active: boolean;
}

type OnlyNumbers = PickByType<Mixed, number>;
// Type: { id: number; age: number; }
4. テンプレートリテラル型

目的: パターンマッチングと変換を備えた文字列ベースの型を作成します。

基本的なテンプレートリテラル:

type EventName = "click" | "focus" | "blur";
type EventHandler = `on${Capitalize<EventName>}`;
// Type: "onClick" | "onFocus" | "onBlur"

文字列の操作:

type UppercaseGreeting = Uppercase<"hello">; // "HELLO"
type LowercaseGreeting = Lowercase<"HELLO">; // "hello"
type CapitalizedName = Capitalize<"john">; // "John"
type UncapitalizedName = Uncapitalize<"John">; // "john"

パスの構築:

type Path<T> = T extends object
  ? {
      [K in keyof T]: K extends string ? `${K}` | `${K}.${Path<T[K]>}` : never;
    }[keyof T]
  : never;

interface Config {
  server: {
    host: string;
    port: number;
  };
  database: {
    url: string;
  };
}

type ConfigPath = Path<Config>;
// Type: "server" | "database" | "server.host" | "server.port" | "database.url"
5. ユーティリティ型

組み込みのユーティリティ型:

// Partial<T> - Make all properties optional
type PartialUser = Partial<User>;

// Required<T> - Make all properties required
type RequiredUser = Required<PartialUser>;

// Readonly<T> - Make all properties readonly
type ReadonlyUser = Readonly<User>;

// Pick<T, K> - Select specific properties
type UserName = Pick<User, "name" | "email">;

// Omit<T, K> - Remove specific properties
type UserWithoutPassword = Omit<User, "password">;

// Exclude<T, U> - Exclude types from union
type T1 = Exclude<"a" | "b" | "c", "a">; // "b" | "c"

// Extract<T, U> - Extract types from union
type T2 = Extract<"a" | "b" | "c", "a" | "b">; // "a" | "b"

// NonNullable<T> - Exclude null and undefined
type T3 = NonNullable<string | null | undefined>; // string

// Record<K, T> - Create object type with keys K and values T
type PageInfo = Record<"home" | "about", { title: string }>;

詳細な実例とパターン

詳細なセクション(## Advanced Patterns で始まるもの)は references/details.md にあります。上記のナビゲーション概要で不十分な場合は、そのファイルを参照してください。

ベストプラクティス

  1. any より unknown を使う: 型チェックを徹底する
  2. オブジェクトの形状には interface を優先する: エラーメッセージがより分かりやすくなる
  3. ユニオンや複雑な型には type を使う: より柔軟になる
  4. 型推論を活用する: 可能な場合は TypeScript に推論させる
  5. ヘルパー型を作成する: 再利用可能な型ユーティリティを構築する
  6. const アサーションを使う: リテラル型を保持する
  7. 型アサーションを避ける: 代わりに型ガードを使う
  8. 複雑な型を文書化する: JSDoc コメントを追加する
  9. strict モードを使う: すべての strict コンパイラーオプションを有効にする
  10. 型をテストする: 型テストを使って型の挙動を検証する

型のテスト

// Type assertion tests
type AssertEqual<T, U> = [T] extends [U]
  ? [U] extends [T]
    ? true
    : false
  : false;

type Test1 = AssertEqual<string, string>; // true
type Test2 = AssertEqual<string, number>; // false
type Test3 = AssertEqual<string | number, string>; // false

// Expect error helper
type ExpectError<T extends never> = T;

// Example usage
type ShouldError = ExpectError<AssertEqual<string, number>>;

よくある落とし穴

  1. any の使いすぎ: TypeScript の目的が損なわれる
  2. strict null チェックの無視: 実行時エラーにつながる可能性がある
  3. 複雑すぎる型: コンパイルが遅くなる可能性がある
  4. 判別可能なユニオンを使わない: 型の絞り込みの機会を逃す
  5. readonly 修飾子の忘れ: 意図しない変更を許してしまう
  6. 循環する型参照: コンパイラーエラーを引き起こす可能性がある
  7. エッジケースを処理しない: 空の配列や null 値など

パフォーマンスに関する考慮事項

  • 深くネストした条件型を避ける
  • 可能な場合はシンプルな型を使う
  • 複雑な型の計算をキャッシュする
  • 再帰型の再帰の深さを制限する
  • ビルドツールを使って本番環境では型チェックをスキップする

コメント