mcpField
T
typescript-strict-mode

typescript-strict-mode

github.com/op0ai開発17293 回インストール41 週間前に更新
リポジトリ

typescript-strict-mode について

厳密で安全なコードを目指す開発者向けの TypeScript ガイドです。`any` の代替、型の絞り込み、再利用可能な型、ESLint ルールを解説します。

主な機能

  1. 01`any` をより安全な型に置き換え
  2. 02型ガードによる `unknown` の絞り込み
  3. 03ジェネリクスとユニオン型の活用
  4. 04コールバックと非同期関数の型付け
  5. 05厳格な ESLint ルールの設定

活用シーン

  1. 01TypeScript の安全でない型を確認
  2. 02JSON データや動的オブジェクトを型付け
  3. 03コードベースに厳格な lint ルールを設定

インストール

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

$npx skills add https://github.com/op0ai/claude-skills --skill typescript-strict-mode

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

詳細

TypeScript 厳格モードのベストプラクティス

概要

このスキルでは、あらゆるフレームワークに適用できる、厳格な TypeScript のプラクティスを紹介します。any の回避、適切な型注釈の使用、そしてより安全で保守しやすいコードを実現するための TypeScript の型システムの活用に焦点を当てます。

最も重要なルール:any は絶対に使わない

重要なルール: 多くのコードベースでは、@typescript-eslint/no-explicit-any が有効になっています。any を使うとビルドが失敗します。

any が危険な理由:

  • TypeScript の型システムの目的を損ないます
  • コンパイル時に検出できるはずのバグを隠します
  • コードベース全体に型の安全性の欠如を波及させます
  • リファクタリングが難しくなり、エラーが発生しやすくなります

any の代替手段

1. 具体的な型を使う

❌ 誤り:

function processData(data: any) { ... }
const items: any[] = [];

✅ 正しい例:

function processData(data: { id: string; name: string }) { ... }
const items: string[] = [];
2. 型が本当に不明な場合は unknown を使う

unknown は any に対する型安全な代替です。使用する前に型を絞り込む必要があります。

❌ 誤り:

function handleResponse(response: any) {
  return response.data.name; // No type checking!
}

✅ 正しい例:

function handleResponse(response: unknown) {
  if (
    typeof response === "object" &&
    response !== null &&
    "data" in response &&
    typeof (response as { data: unknown }).data === "object"
  ) {
    const data = (response as { data: { name: string } }).data;
    return data.name;
  }
  throw new Error("Invalid response format");
}
3. 再利用可能なコンポーネントにはジェネリクスを使う

❌ 誤り:

function wrapValue(value: any): { wrapped: any } {
  return { wrapped: value };
}

✅ 正しい例:

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

// Usage
const wrappedString = wrapValue("hello"); // { wrapped: string }
const wrappedNumber = wrapValue(42); // { wrapped: number }
4. 複数の可能性にはユニオン型を使う

❌ 誤り:

function handleInput(input: any) {
  if (typeof input === 'string') { ... }
  if (typeof input === 'number') { ... }
}

✅ 正しい例:

function handleInput(input: string | number) {
  if (typeof input === 'string') { ... }
  if (typeof input === 'number') { ... }
}
5. 実行時のチェックには型ガードを使う
interface User {
  id: string;
  name: string;
  email: string;
}

function isUser(value: unknown): value is User {
  return (
    typeof value === "object" &&
    value !== null &&
    "id" in value &&
    "name" in value &&
    "email" in value &&
    typeof (value as User).id === "string" &&
    typeof (value as User).name === "string" &&
    typeof (value as User).email === "string"
  );
}

function processUser(data: unknown) {
  if (isUser(data)) {
    // data is now typed as User
    console.log(data.name);
  }
}
6. 動的オブジェクトには Record<K, V> を使う

❌ 誤り:

const cache: any = {};
cache["key"] = "value";

✅ 正しい例:

const cache: Record<string, string> = {};
cache["key"] = "value";

// Or with specific keys
const userSettings: Record<"theme" | "language", string> = {
  theme: "dark",
  language: "en",
};
7. 柔軟なオブジェクトにはインデックスシグネチャを使う
interface Config {
  name: string;
  version: string;
  [key: string]: string | number | boolean; // Additional properties
}

const config: Config = {
  name: "my-app",
  version: "1.0.0",
  debug: true,
  port: 3000,
};

よく使われるイベントハンドラーの型

React のイベント型
// Form events
const handleSubmit = (e: React.FormEvent<HTMLFormElement>) => {
  e.preventDefault();
  // ...
};

// Input events
const handleChange = (e: React.ChangeEvent<HTMLInputElement>) => {
  const value = e.target.value;
  // ...
};

// Click events
const handleClick = (e: React.MouseEvent<HTMLButtonElement>) => {
  // ...
};

// Keyboard events
const handleKeyDown = (e: React.KeyboardEvent<HTMLInputElement>) => {
  if (e.key === 'Enter') { ... }
};

// Focus events
const handleFocus = (e: React.FocusEvent<HTMLInputElement>) => {
  // ...
};
DOM のイベント型(React 以外)
// Generic DOM events
document.addEventListener('click', (e: MouseEvent) => { ... });
document.addEventListener('keydown', (e: KeyboardEvent) => { ... });
document.addEventListener('submit', (e: SubmitEvent) => { ... });

Promise と非同期処理の型

非同期関数の型付け
// Function returning a promise
async function fetchUser(id: string): Promise<User> {
  const response = await fetch(`/api/users/${id}`);
  return response.json();
}

// Arrow function variant
const fetchUser = async (id: string): Promise<User> => {
  const response = await fetch(`/api/users/${id}`);
  return response.json();
};
Promise の型パターン
// Promise with explicit type
const userPromise: Promise<User> = fetchUser("123");

// Awaiting with type inference
const user = await fetchUser("123"); // User

// Promise.all with multiple types
const [user, posts] = await Promise.all([fetchUser("123"), fetchPosts("123")]); // [User, Post[]]

関数の型

コールバックの型
// Typed callback parameter
function processItems(
  items: string[],
  callback: (item: string, index: number) => void
) {
  items.forEach(callback);
}

// Alternative: Extract the type
type ItemCallback = (item: string, index: number) => void;

function processItems(items: string[], callback: ItemCallback) {
  items.forEach(callback);
}
オーバーロード関数
// Function overloads for different input/output types
function parse(input: string): object;
function parse(input: Buffer): object;
function parse(input: string | Buffer): object {
  if (typeof input === "string") {
    return JSON.parse(input);
  }
  return JSON.parse(input.toString());
}

型アサーション(控えめに使う)

TypeScript よりも多くのことがわかっている場合に限り、型アサーションを使います。

// DOM element assertion (when you know the element type)
const input = document.getElementById("email") as HTMLInputElement;

// Response data assertion (when you trust the API)
const data = (await response.json()) as ApiResponse;

// Non-null assertion (when you know it's not null)
const element = document.querySelector(".button")!;

警告: 型アサーションは TypeScript のチェックを迂回します。可能な場合は型ガードを優先してください。

ユーティリティ型

組み込みのユーティリティ型
// Partial - all properties optional
type PartialUser = Partial<User>;

// Required - all properties required
type RequiredUser = Required<User>;

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

// Omit - exclude specific properties
type UserWithoutId = Omit<User, "id">;

// Readonly - immutable properties
type ReadonlyUser = Readonly<User>;

// Record - create object type
type UserMap = Record<string, User>;

// ReturnType - extract function return type
type FetchUserReturn = ReturnType<typeof fetchUser>;

// Parameters - extract function parameters
type FetchUserParams = Parameters<typeof fetchUser>;

判別可能なユニオン

複数の関連する型を扱うパターン:

type Result<T> = { success: true; data: T } | { success: false; error: string };

function handleResult<T>(result: Result<T>) {
  if (result.success) {
    // TypeScript knows result.data exists here
    console.log(result.data);
  } else {
    // TypeScript knows result.error exists here
    console.error(result.error);
  }
}

モジュール拡張

元の型を変更せずに拡張します。

// Extend Express Request
declare module "express" {
  interface Request {
    user?: User;
  }
}

// Extend environment variables
declare global {
  namespace NodeJS {
    interface ProcessEnv {
      DATABASE_URL: string;
      API_KEY: string;
    }
  }
}

よくある落とし穴

落とし穴 1:JSON データに any を使う

❌ 誤り:

const data: any = JSON.parse(jsonString);

✅ 正しい例:

interface ExpectedData {
  id: string;
  name: string;
}

const data: unknown = JSON.parse(jsonString);
// Then validate with type guard or schema validation (zod, etc.)
落とし穴 2:コールバック内の暗黙的な any

❌ 誤り:

// 'item' has implicit 'any' type
items.map((item) => item.name);

✅ 正しい例:

items.map((item: Item) => item.name);
// Or ensure 'items' has proper type: Item[]
落とし穴 3:オブジェクトのプロパティへのアクセス

❌ 誤り:

function getValue(obj: any, key: string) {
  return obj[key];
}

✅ 正しい例:

function getValue<T extends Record<string, unknown>, K extends keyof T>(
  obj: T,
  key: K
): T[K] {
  return obj[key];
}
落とし穴 4:空の配列の型

❌ 誤り:

const items = []; // any[]

✅ 正しい例:

const items: string[] = [];
// or
const items: Array<string> = [];

有効にする ESLint ルール

TypeScript を厳格に運用するには、次のルールを有効にします。

{
  "rules": {
    "@typescript-eslint/no-explicit-any": "error",
    "@typescript-eslint/strict-boolean-expressions": "warn",
    "@typescript-eslint/no-unsafe-assignment": "error",
    "@typescript-eslint/no-unsafe-member-access": "error",
    "@typescript-eslint/no-unsafe-call": "error",
    "@typescript-eslint/no-unsafe-return": "error"
  }
}

注:非推奨の @typescript-eslint/no-implicit-any-catch ルールの代わりに、tsconfig.json で useUnknownInCatchVariables: true を設定してください(TypeScript 4.4 以降)。これにより、catch 節の変数が any ではなく unknown として型付けされます。

クイックリファレンス

| any の代わりに | 使用するもの | | ---------------- | ------------------------ | --- | | 不明なデータ | unknown | | 柔軟な型 | ジェネリクス <T> | | 複数の型 | ユニオン A | B | | 動的なキー | Record<K, V> | | null 許容 | T \| null | | オプション | T \| undefined または T? | | コールバック | (args) => ReturnType | | 空の配列 | Type[] | | JSON データ | unknown + 型ガード |

まとめ

  • any は絶対に使わない — TypeScript の目的を損ないます
  • 本当に不明な型には unknown を使い、型ガードで絞り込む
  • 再利用可能で型安全なコンポーネントにはジェネリクスを使う
  • 可能性が有限の場合はユニオン型を使う
  • 複雑な状態機械には判別可能なユニオンを使う
  • 違反を自動的に検出できるよう、厳格な ESLint ルールを有効にする

コメント