mcpField
T
typescript

typescript

github.com/alexander-danilenko開発18101 回インストール2 週間前に更新
リポジトリ

typescript について

コードを書く、または変更する開発者向けに、TypeScript 5+ の規約をまとめたスキルです。型によるドメイン表現、厳密なチェック、パッケージ境界を重視します。

主な機能

  1. 01ブランド型と判別可能なユニオン
  2. 02`satisfies` と定数オブジェクトの優先
  3. 03`any` の回避と型アサーションの隔離
  4. 04型専用インポートと内部の型推論
  5. 05宣言ファイルとプロジェクト参照
  6. 06厳密な tsconfig とモジュール解決

活用シーン

  1. 01ドメイン型と状態処理の設計
  2. 02TypeScript の変更内容のレビュー
  3. 03コンパイラーとモジュール設定

インストール

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

$npx skills add https://github.com/alexander-danilenko/cortex-claude-code-plugins --skill typescript

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

詳細

TypeScript

TypeScript 5 以降のプロジェクト共通の規約です。新しく記述するコードや変更するコードに適用してください。依頼がない限り、変更していないファイルを規約に合わせてリファクタリングしないでください。

規約

  • チェックではなく、型でドメインをモデル化します。 ブランド型(type UserId = string & { readonly __brand: 'UserId' })や判別可能なユニオンを使うと、エラーをコンパイル時に検出でき、コストもかかりません。実行時ガードでは、同じバグを本番環境で検出することになります。
  • 状態を持つものには判別可能なユニオンを使います — リクエストのライフサイクル、フォームの状態、解析結果などです。これにより、コンパイラーがすべての状態の処理を強制するため、新しいバリアントを追加すると、暗黙のフォールスルーではなくエラーとして表面化します。
  • as より satisfies を使います。 satisfies は狭く推論されたリテラル型を保ちながら型との適合性を検証しますが、as はチェックを無効にします。as を使うのは、コンパイラーが把握できないことを確信している場合に限り、理由をコメントに記載してください。
  • enum より定数オブジェクトを使います。 as const オブジェクトは、コードが出力されず、名目型に関する予期しない動作もない通常の値です。一方、enum は実行時コードを生成し、const enum は isolatedModules で問題を起こします。
  • any はバグの報告です。 unknown を使い、型を絞り込んでください。境界で any が本当に避けられない場合は、呼び出し箇所へ広がらないよう、1 つのアダプター関数内に隔離してください。
  • 型専用インポートを分けます(import type { … })。バンドラーは型専用インポートを問題なく消去します。通常のインポートと混在させると、削除したかった実行時依存関係が残ることがあります。
  • 型推論を活用します。 エクスポートするシグネチャやパブリックなシグネチャには型注釈を付け、内部のものは型推論に任せてください。注釈の付けすぎはノイズになり、実際の型から乖離していきます。
  • ライブラリーとして利用されるものには .d.ts を提供します(declaration: true)。これがないと、利用者側では any にフォールバックし、型付けの成果がパッケージの境界で止まってしまいます。
  • モノレポでは、プロジェクト参照と composite を使います。 インクリメンタルビルドでは変更されたものだけを再ビルドします。単一のフラットな tsconfig では、編集のたびにすべてが再コンパイルされます。

押さえておきたい 2 つのイディオム

網羅性は、強制しなければ実際には保証されません。ユニオンに対する switch は、新しいバリアントが追加されてもコンパイルに成功します。assertNever を使うことで、更新が必要なすべての箇所でビルドエラーになるようにできます。

function assertNever(x: never): never {
  throw new Error(`Unhandled variant: ${JSON.stringify(x)}`);
}

function area(s: Shape): number {
  switch (s.kind) {
    case "circle":
      return Math.PI * s.r ** 2;
    case "rect":
      return s.w * s.h;
    default:
      return assertNever(s); // add a variant → compile error here
  }
}

ブランド型は、それを取得する唯一の方法が検証を行う関数である場合にのみ、導入する価値があります。

type Brand<T, B> = T & { readonly __brand: B };
type Email = Brand<string, "Email">;

export function toEmail(raw: string): Email {
  if (!raw.includes("@")) throw new Error(`Not an email: ${raw}`);
  return raw as Email;
}

コンストラクター内の as は、唯一正当な使い方です。未検証の値を検証済みの値に変換する境界だからです。それ以外の場所で Email にキャストすると、この型を導入した意味がなくなります。

tsconfig の基本設定

strict: true で有効になるのは、必要な機能のすべてではありません。デフォルトの設定では見逃される種類のバグをそれぞれ検出できるため、以下を追加してください。

{
  "compilerOptions": {
    "strict": true,
    "noUncheckedIndexedAccess": true,
    "noPropertyAccessFromIndexSignature": true,
    "noImplicitOverride": true,
    "noImplicitReturns": true,
    "noFallthroughCasesInSwitch": true,
    "noUnusedLocals": true,
    "noUnusedParameters": true,
    "isolatedModules": true,
    "declaration": true,
    "skipLibCheck": true
  }
}

noUncheckedIndexedAccess は最も効果が高い設定であり、導入時の影響も最も大きくなります。これを有効にすると、arr[i] の戻り値は実態どおり T | undefined になります。新しいプロジェクトでは有効にし、既存のプロジェクトでは移行が必要になることを想定してください。

モジュール解決には、Node が出力を実行する場合は NodeNext を、バンドラーが実行する場合は bundler を使います。

コメント