有时候我们希望一个接口允许有任意的属性、TypeScript给我们提供了两种索引签名:字符串和数字。
比如:
interface Person{
name: string;
age: number;
[propName: string]: any;
}
let tom: Person = {
name: 'Tom',
gender: 'male'
};
使用 [propName: string] 定义了任意属性取string类型的值
又比如:
interface IPerson {
name: string;
age?: number;
[propName: string]: string;
}
let tom: IPerson = {
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'.
注意了:
1. 一旦定义了任意属性 且 任意属性的类型为string时、那么确定属性和可选属性的类型都必须是它的类型的子集
👆🌰任意属性的值允许是 string,但是可选属性 age 的值却是 number,number 不是 string 的子属性,所以报错了。
此时 { name: 'Tom', age: 25, gender: 'male' } 的类型被推断成了 { [x: string]: string | number; name: string; age: number; gender: string; }
2. 一个接口中只能定义一个相同类型的任意属性。如果接口中有多个类型的任意属性,则可以在任意属性中使用联合类型
所以可以改写为:
interface IPerson {
name: string;
age?: number;
[propName: string]: string | number;
}
let tom: IPerson = {
name: 'Tom',
age: 25,
gender: 'male'
};
but...要注意👇🌰这样是不会报错的:
interface IArgument {
[index: number]: number;
a: number;
b: string;
c: Function;
}
// 这样是ok的 ???
3. number类型的任意属性签名不会影响string类型
vs
而👇🌰这样是会报错的:
interface IArgument {
[index: string]: number;
a: number;
b: Function;
}
// 这样是会报错的 ???
因为 b: Function 不是任意属性的值类型(number)的子集、又回到了第一点的约束
看出区别了吗?
那我们可以同时使用两种类型的索引吗? 可以
可以同时使用两种类型的索引,但是数字索引的值类型必须是字符串索引的值类型的子类型。 当使用 number 来索引时,JavaScript会将它转换成 string 然后再去索引对象。 也就是说用 1 去索引等同于使用 "1" 去索引,因此两者需要保持一致。
🌰:
interface ITest{
[prop: string]: object;
[index: number]: Function;
}
and
interface ITest{
[prop: string]: string;
[index: number]: Function;
}
// Numeric index type 'Function' is not assignable to string index type 'string'.
因此:
两种任意类型签名并存时,number 类型的签名指定的值类型必须是 string 类型的签名指定的值类型的子集
一旦定义了 ‘索引为字符串类型签名’ 的任意属性,那么其他属性(确定属性、可选属性、只读属性等)的类型都必须是它的 ‘值类型’ 的子集。