mcpField
I
iterate-objects-safely

iterate-objects-safely

github.com/marius-townhouse開発645 回インストール39 週間前に更新
リポジトリ

iterate-objects-safely について

`any` や型アサーションに頼りすぎず、安全にオブジェクトを反復する方法を扱う TypeScript スキルです。データの形に応じて `Object.entries`、既知キーの配列、アサーション、`Map` を使い分けられます。

主な機能

  1. 01一般的な反復には `Object.entries`
  2. 02既知のキーを明示
  3. 03閉じたオブジェクトに限りキーをアサート
  4. 04型安全な走査に `Map`
  5. 05継承プロパティを考慮

活用シーン

  1. 01`for...in` のインデックスエラーを修正
  2. 02オブジェクトのキーと値を反復
  3. 03オブジェクトと `Map` の選択
  4. 04追加プロパティを持つオブジェクトの処理

インストール

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

$npx skills add https://github.com/marius-townhouse/effective-typescript-skills --skill iterate-objects-safely

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

詳細

オブジェクトの反復処理方法を理解する

概要

TypeScript でオブジェクトを反復処理するのは、意外と難しいものです。for...in ループでは、キーはオブジェクトのキーではなく string と推論されるため、インデックス参照でエラーが発生します。これは、オブジェクトが宣言された型にないプロパティを持つことがあり(構造的型付け)、for...in が継承されたプロパティも含めるためです。

安全な反復処理のパターンを理解すれば、オブジェクトを正しく走査しながら、any 型や型アサーションを避けられます。

このスキルを使う場面

  • オブジェクトのキーと値を反復処理する
  • for...in ループで「Element implicitly has 'any' type」エラーが発生する
  • Object.entries が値の型として any を返す
  • 既知と未知の両方のオブジェクトプロパティを扱う必要がある
  • データ保存に Map とオブジェクトのどちらを使うか検討する

絶対原則

あらゆるオブジェクトを安全に反復処理するには Object.entries を使います。正確な構造がわかっている場合に限り、keyof アサーションとともに for...in を使います。型安全性を確実にするには Map を検討してください。

検出

次のエラーに注意してください。

// ERROR: Element implicitly has 'any' type
for (const k in obj) {
  const v = obj[k];  // Error!
}

// ERROR: Type 'string' cannot be used to index type 'ABC'
function foo(abc: ABC) {
  for (const k in abc) {
    const v = abc[k];  // Error!
  }
}

問題点

interface ABC {
  a: string;
  b: string;
  c: number;
}

function foo(abc: ABC) {
  for (const k in abc) {
    // k is string, not 'a' | 'b' | 'c'
    const v = abc[k];
    //     ^? any - TypeScript gives up
  }
}

// Why? Structural typing allows extra properties:
const x = { a: 'a', b: 'b', c: 2, d: new Date() };
foo(x);  // Valid! x has at least ABC's properties

// k could be 'd', which isn't in ABC

Object.entries による安全な反復処理

function foo(abc: ABC) {
  for (const [k, v] of Object.entries(abc)) {
    // k: string (honest about what it is)
    // v: any (honest about unknown values)
    console.log(k, v);
  }
}

メリット:常に安全で、型アサーションは不要です デメリット:値は any、キーは string です

既知のキーを使った反復処理

存在するキーが正確にわかっている場合:

const obj = { one: 'uno', two: 'dos', three: 'tres' };

// Explicitly list keys
const keys = ['one', 'two', 'three'] as const;
for (const k of keys) {
  // k: 'one' | 'two' | 'three'
  const v = obj[k];  // string - precise!
}

クローズドなオブジェクトに対する型アサーション

オブジェクトの構造を確実に把握している場合:

const obj = { one: 'uno', two: 'dos', three: 'tres' };

for (const kStr in obj) {
  const k = kStr as keyof typeof obj;
  // k: 'one' | 'two' | 'three'
  const v = obj[k];  // OK
}

警告:オブジェクトの生成を自分で管理し、余分なプロパティがないとわかっている場合に限り安全です。

Map:型安全な代替手段

const m = new Map([
  ['one', 'uno'],
  ['two', 'dos'],
  ['three', 'tres'],
]);

for (const [k, v] of m) {
  // k: string (known type)
  // v: string (known type)
  console.log(k, v);
}

メリット:型が保証され、プロトタイプ汚染もありません デメリット:JSON データにはやや不便で、API も異なります

実例

interface Config {
  host: string;
  port: number;
  ssl: boolean;
}

// Safe iteration with Object.entries
function printConfig(config: Config) {
  for (const [key, value] of Object.entries(config)) {
    console.log(`${key}: ${value}`);
  }
}

// Type-safe with known keys
function validateConfig(config: Config): string[] {
  const errors: string[] = [];
  const requiredKeys = ['host', 'port', 'ssl'] as const;
  
  for (const key of requiredKeys) {
    if (!(key in config)) {
      errors.push(`Missing: ${key}`);
    }
  }
  
  return errors;
}

プレッシャー下での対処プロトコル

オブジェクトの反復処理で型の問題が発生した場合:

  1. Object.entries を試す:最も安全な方法で、あらゆるオブジェクトに使えます
  2. Map を検討する:データ構造を自分で管理できる場合
  3. キーを明示的に列挙する:コンパイル時にキーがわかっている場合
  4. 型アサーションは控えめに使う:オブジェクトの構造を確実に把握している場合に限ります

危険信号

アンチパターン問題解決策
for...in 内の obj[k]k は string であり、安全なインデックスではありませんObject.entries を使う
パラメーターに対する as keyof Tオブジェクトに余分なキーがある可能性がありますObject.entries または Map
継承されたプロパティを無視するプロトタイプ汚染のリスクがありますObject.entries はこれらを除外します

よくある言い訳

「as any を使えばいい」

実際は:Object.entries も同じ any を返しますが、その事実が明確です。型安全性を回避する必要はありません。

「このオブジェクトに余分なプロパティが追加されることはない」

実際は:TypeScript の構造的型付けでは、追加される可能性があります。前提条件を明示してください。

「Map は冗長すぎる」

実際は:確かに冗長ですが、型安全性も高まります。重要なコードではその価値があります。

クイックリファレンス

方法キーの型値の型安全性
for...instringany(エラー)安全ではない
Object.entriesstringany安全
既知のキーの配列固有固有安全
keyof アサーション固有固有リスクあり
Map固有固有安全

要点

構造的型付けとプロトタイプ汚染のため、オブジェクトの反復処理には注意が必要です。安全性を重視するなら Object.entries、正確さを重視するなら明示的なキー配列、型安全性を確実にするなら Map を使ってください。

参考資料

  • 『Effective TypeScript, 2nd Edition』Dan Vanderkam 著
  • 項目 60:オブジェクトの反復処理方法を理解する

コメント