カスタム・タイピングを行う場合、任意のプロパティ・シグネチャを許可するインターフェースが必要になることがあります。
一方の属性シグネチャは文字列型で、もう一方の属性シグネチャは数値型です。
string 型の任意の属性
まず、属性シグネチャが文字列の場合は、例えばオブジェクトの属性です:
interface A {
[prop: string]: number;
}
const obj: A = {
a: 1,
b: 3,
};
[prop: string]: number stringはオブジェクトのキーがすべてstring型であることを意味し、numberは属性値の型を指定します。
propは関数の正式なパラメータに似ており、他の名前で呼び出すことができます。
string 型の任意の属性
2つ目のタイプは、属性シグネチャが配列の添え字のような数値型である場合です:
interface B {
[index: number]: string;
}
const arr: B = ['suukii'];
[index: number]: string B型の配列は任意の数値添え字を持つことができ、配列のメンバは文字列型でなければならないことを意味します。
同様に、indexは関数の正式なパラメータのようなもので、他の識別子を使ってもまったく問題ありません。
同時に2つの任意のプロパティを定義します。
インターフェースはこれら両方の任意の属性を定義することができますが、数値型のシグネチャで指定される値型は、例として文字列型のシグネチャで指定される値型のサブセットでなければならないことに注意してください:
interface C {
[prop: string]: number;
[index: number]: string;
}
// Numeric index type 'string' is not assignable to string index type 'number'.
なぜなら、indexは値型として文字列を指定し、propは値型として数値を指定し、文字列は数値のサブセットではないからです。
これを次のように置き換えても、Functionはobjectのサブセットなので、定義は成り立ちます:
interface C {
[prop: string]: object;
[index: number]: Function;
}
任意のプロパティと他の型のプロパティの両方を定義します。
もう一つ注意すべき点は、一度属性が定義されると、他のすべての属性の型はその型のサブセットでなければならないということです。
例えば、必須の属性 name とオプションの属性 age、さらに文字列型のその他の属性シグニチャを持つ Person インターフェースが必要だとします。この場合、Person インターフェースは次のように定義します:
interface Person {
name: string;
age?: number;
[prop: string]: string;
}
// Property 'age' of type 'number' is not assignable to string index type 'string'.
[prop: string]: string しかし、この定義は、他の属性の型も文字列でなければならないという指定が存在するため、有効ではありません:
interface Person {
name: string;
age?: number;
[prop: string]: string | number;
}
番号型の属性シグネチャについても同様です:
type MyArray = {
0: string;
[index: number]: number;
};
// Property '0' of type 'string' is not assignable to numeric index type 'number'.
しかし、番号型の任意の属性シグネチャは、他の文字列型の属性シグネチャには影響しません:
type Arg = {
[index: number]: number;
length: string;
};
上記のように、number型の属性の型はnumberと指定されていますが、length属性はstring型のシグネチャなので、前者の影響を受けません。




