任意属性
有时候我们希望一个接口允许有任意的属性,可以使用如下方式:
interface Person {name: string;age?: number;[propName: string]: any;}let tom: Person = { name: "Tom", gender: "male" };
使用 [propName: string] 定义了任意属性取 string 类型的值。propName 类似于函数的形参,是可以取其他名字的。
一个接口中只能定义一个任意属性。如果接口中有多个类型的属性,则可以在任意属性中使用联合类型:
interface Person {name: string;age?: number;[propName: string]: string | number;}let tom: Person = { name: "Tom", age: 25, gender: "male" };
同时定义两种任意属性
需要注意的是,一个接口可以同时定义这两种任意属性,但是 number 类型的签名指定的值类型必须是 string 类型的签名指定的值类型的子集,举个例子:
interface C {[prop: string]: number;[index: number]: string;}// Numeric index type 'string' is not assignable to string index type 'number'.
上面定义是不成立的,因为 index 指定的值类型是 string,而 prop 指定的值类型是 number,string 并不是 number 的子集。
如果换成下面这样,定义就是成立的,因为 Function 是 object 的子集:
interface C {[prop: string]: object;[index: number]: Function;}
同时定义任意属性和其他类型的属性
需要注意的是,一旦定义了任意属性,那么其他属性(确定属性、可选属性、只读属性等)的类型都必须是它的类型的子集:
interface Person {name: string;age?: number;[propName: string]: string;}let tom: Person = {name: 'Tom',age: 25,gender: 'male'};// index.ts(3,5): error TS2411: Property 'age' of type 'number' is not assignable to string index type 'string'.// index.ts(7,5): error TS2322: Type '{ [x: string]: string | number; name: string; age: number; gender: string; }' is not assignable to type 'Person'.// Index signatures are incompatible.// Type 'string | number' is not assignable to type 'string'.// Type 'number' is not assignable to type 'string'.
但是,number 类型的任意属性签名不会影响其他 string 类型的属性签名:
type Arg = {[index: number]: number;length: string;};
但是反过来就不一样了,如果接口定义了 string 类型的任意属性签名,它不仅会影响其他 string 类型的签名,也会影响其他 number 类型的签名。这一点可以参考两种任意类型签名并存时,number 类型的签名指定的值类型必须是 string 类型的签名指定的值类型的子集这句话。
