インデックスシグネチャと矛盾する型のプロパティを宣言するとエラーになる
[key: string]: numberのようなインデックスシグネチャを持つinterfaceでは、個別に宣言する具体的なプロパティも、インデックスシグネチャの型と互換性がなければ宣言できません。
エラーメッセージの読み方
main.ts(3,3): error TS2411: Property 'name' of type 'string' is not assignable to 'string' index type 'number'.
main.ts- ファイル名
3- 行番号
3- 列番号 — この位置でtscが検査に失敗しました
TS2411- エラーコード — 検索するとTypeScriptの解説が見つかります
Property 'name' of type 'string' is not assignable to 'string' index type 'number'.- 内容 — 期待していたもの、または受け付けられなかったもの
このエラーが出る典型パターン
パターン1
1 interface Dict { 2 [key: string]: number; 3 name: string ; ^ 4 } 5 console.log("ok");
インデックスシグネチャは全プロパティがnumber型であることを要求します。nameをstring型で宣言すると矛盾します。
直し方: name: string を count: number にします。
パターン2
1 interface Scores { 2 [key: string]: number; 3 label: boolean; ^ 4 } 5 console.log("ok");
labelをboolean型で宣言すると、number型を要求するインデックスシグネチャと矛盾します。
直し方: label: boolean を total: number にします。
パターン3
1 interface Flags { 2 [key: string]: boolean; 3 count: number ; ^ 4 } 5 console.log("ok");
このインデックスシグネチャはboolean型を要求します。countをnumber型で宣言すると矛盾します。
直し方: count: number を active: boolean にします。
パターン4
1 interface Prices { 2 [key: string]: number; 3 label: string; ^ 4 } 5 console.log("ok");
インデックスシグネチャは全プロパティがnumber型であることを要求します。labelをstring型で宣言すると矛盾します。
直し方: label: string を base: number にします。
パターン5
1 interface Toggles { 2 [key: string]: boolean; 3 level: number ; ^ 4 } 5 console.log("ok");
このインデックスシグネチャはboolean型を要求します。levelをnumber型で宣言すると矛盾します。
直し方: level: number を enabled: boolean にします。
よくある誤解
「個別に書いたプロパティは、インデックスシグネチャより優先されるはず」という考えは誤りです。TypeScriptは両方が矛盾なく共存できることを要求し、型が食い違う時点でinterfaceの宣言自体をエラーにします。
実務での勘所
この制約は恣意的なルールではなく、型システムの一貫性を保つための論理的な必然です。[key: string]: numberと宣言すると、「文字列であるすべてのキーの値はnumber型である」という約束になります。個別に宣言したプロパティ名も文字列である以上、この約束の対象から逃れられません。もし個別のプロパティにだけ違う型を許してしまうと、obj.knownPropという書き方とobj["knownProp"]という動的な書き方で、同じ値のはずなのに型が食い違うという矛盾が生まれてしまいます。