iterate-objects-safely について
`any` や型アサーションに頼りすぎず、安全にオブジェクトを反復する方法を扱う TypeScript スキルです。データの形に応じて `Object.entries`、既知キーの配列、アサーション、`Map` を使い分けられます。
主な機能
- 01一般的な反復には `Object.entries`
- 02既知のキーを明示
- 03閉じたオブジェクトに限りキーをアサート
- 04型安全な走査に `Map`
- 05継承プロパティを考慮
活用シーン
- 01`for...in` のインデックスエラーを修正
- 02オブジェクトのキーと値を反復
- 03オブジェクトと `Map` の選択
- 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;
}
プレッシャー下での対処プロトコル
オブジェクトの反復処理で型の問題が発生した場合:
- Object.entries を試す:最も安全な方法で、あらゆるオブジェクトに使えます
- Map を検討する:データ構造を自分で管理できる場合
- キーを明示的に列挙する:コンパイル時にキーがわかっている場合
- 型アサーションは控えめに使う:オブジェクトの構造を確実に把握している場合に限ります
危険信号
| アンチパターン | 問題 | 解決策 |
|---|---|---|
for...in 内の obj[k] | k は string であり、安全なインデックスではありません | Object.entries を使う |
パラメーターに対する as keyof T | オブジェクトに余分なキーがある可能性があります | Object.entries または Map |
| 継承されたプロパティを無視する | プロトタイプ汚染のリスクがあります | Object.entries はこれらを除外します |
よくある言い訳
「as any を使えばいい」
実際は:Object.entries も同じ any を返しますが、その事実が明確です。型安全性を回避する必要はありません。
「このオブジェクトに余分なプロパティが追加されることはない」
実際は:TypeScript の構造的型付けでは、追加される可能性があります。前提条件を明示してください。
「Map は冗長すぎる」
実際は:確かに冗長ですが、型安全性も高まります。重要なコードではその価値があります。
クイックリファレンス
| 方法 | キーの型 | 値の型 | 安全性 |
|---|---|---|---|
for...in | string | any(エラー) | 安全ではない |
Object.entries | string | any | 安全 |
| 既知のキーの配列 | 固有 | 固有 | 安全 |
keyof アサーション | 固有 | 固有 | リスクあり |
| Map | 固有 | 固有 | 安全 |
要点
構造的型付けとプロトタイプ汚染のため、オブジェクトの反復処理には注意が必要です。安全性を重視するなら Object.entries、正確さを重視するなら明示的なキー配列、型安全性を確実にするなら Map を使ってください。
参考資料
- 『Effective TypeScript, 2nd Edition』Dan Vanderkam 著
- 項目 60:オブジェクトの反復処理方法を理解する