在大多数情况下,我们在使用对象前就可以确定对象的结构,并为对象中的属性添加准确的类型。
但是当我们无法确定对象中有哪些属性时,或者说对象中可以出现任意多个属性时,此时,我们就可以用索引签名类型了。
索引签名,主要是用来规定索引和属性值的类型,索引类型只能是 string 类型 或者 number 类型 或者 symbol 类型。
在 TypeScript 中,索引签名类型包括字符串索引签名类型 和数字索引签名类型。
1. 字符串索引签名类型
1.1. 语法格式
typescript
interface MyObj {
[key: string]: number;
}
上面的这段代码中,使用 [key: string] 来约束该接口中允许出现的属性名称,其中 key 表示字符串索引的标识符,可以是任何有效的属性名,而 number 表示对应属性的值的类型,可以是任意类型。
1.2. 用法
typescript
interface MyObj {
[key: string]: number;
}
let obj: MyObj = {
a: 1,
b: 2,
c: 3,
d: 4
};
如果对象中属性值的类型不是 number 类型,代码会报错。
typescript
interface MyObj {
[key: string]: number;
}
let obj: MyObj = {
a: 1, // 正确
b: 2, // 正确
name: "Echo", // 报错:不能将类型"string"分配给类型"number"
bool: true, // 报错:不能将类型"boolean"分配给类型"number"
};
1.3. 特点
- 一旦定义了索引签名,那么必选属性和可选属性的类型都必须是它的类型的子集。
- 返回的属性值的类型必须是索引签名指定的类型。
- 可以与其他明确声明的属性共存。
- 可以同时拥有多种类型的索引签名。
typescript
// Ok
interface Person {
// 同时拥有 string 类型和 number 类型的索引签名
[props: string]: string | number;
// 可以定义其它属性,但是属性值的类型必须是索引签名类型的子集
// 这里的属性值类型必须是 string 类型或者 number 类型
name?: string;
age: number;
}
// Error
interface User {
[props: string]: string | number;
name?: string;
age: number;
// 报错:类型"true"的属性"bool"不能赋给"string"索引类型"string | number"
// 由于上面的索引签名规定了只能是 string 类型或者 number 类型,所以这里不能写其它类型
bool: true;
}
- 可以访问对象中已存在的属性,如果访问对象中不存在的属性,会返回 undefined。
typescript
interface Person {
[props: string]: string | number;
name: string;
age: number;
}
const person: Person = {
name: "Echo",
age: 26,
city: "GuangZhou",
phone: 13000000000,
gender: "Male",
};
// 访问存在的属性
console.log(person.name); // 输出:Echo
console.log(person["age"]); // 输出:26
console.log(person.city); // 输出:GuangZhou
console.log(person["phone"]); // 输出:13000000000
console.log(person["gender"]); // 输出:Male
// 访问不存在的属性
console.log(person["address"]); // 输出:undefined
2. 数字索引签名类型
当一个对象具有数字索引签名时,它意味着该对象可以使用数字来索引并获得相应的属性的值。
2.1. 语法
typescript
interface MyArr {
[index: number]: string
}
上面的这段代码中,我们定义了 MyArr 接口,它具有索引签名。这个索引签名表示了当用 number 去索引 MyArr 时会得到 string 类型的返回值。
2.2. 用法格式
typescript
interface MyArr {
[index: number]: string;
}
const arr: MyArr = ["Echo", "James", "John", "Steven"];
console.log(arr[0]); // Echo
console.log(arr[1]); // James
console.log(arr[2]); // John
console.log(arr[3]); // Steven
2.3. 特点
- 可以同时拥有多种类型的索引签名。
- 返回的属性值的类型必须是索引签名指定的类型。
typescript
interface MyArr {
[index: number]: string | number;
}
const arr: MyArr = [1, 'a', 2, 'b'];
3. 注意事项
3.1. 索引签名的键只能是 string、number、symbol 或者模板文本类型

3.2. 不能多次定义相同类型的索引签名

4. 索引签名类型 VS 泛型工具类型Record<K, T>
先看看下面的代码,我们发现索引签名类型和泛型工具类型 Record<K, T> 很相似:
typescript
const object1: Record<string, string> = { name: "Echo" }; // 正确
const object2: { [key: string]: string } = { name: "Echo" }; // 正确
但实际上它们两者之间还是有区别的:
4.1. 用途不同
- 索引签名类型:主要用于定义具有动态属性的对象,允许通过字符串或数字索引来访问属性。
typescript
interface MyObj {
[key: string]: string | number
}
const obj: MyObj = {
name: "Echo",
age: 26
}
console.log(obj.name); // Echo
console.log(obj["age"]); // 26
- Record<K, T>:主要用于生成一个新的类型,用于表示具有固定类型的属性集合,其中所有属性的值都具有相同的类型。
typescript
interface Person {
name: string;
gender: string;
}
type NewPerson = Record<keyof Person, string>;
const person: NewPerson = {
name: "Echo",
gender: "Male",
}
4.2. 类型约束
- 索引签名类型:对于索引签名,键的类型可以是 string、number、symbol 或者模板文本类型,值的类型可以是任意类型。

- Record<K, T>:对于 Record<K, T>,类型参数 K 的类型可以是 string、number 或 symbol,可以接收字面量类型或者字面量类型组成的联合类型来作为参数,而类型参数 T 可以是任意类型。
比如使用联合类型来作为 Key 值:
typescript
type Person = Record<"name" | "age" | "gender" | "bool", string | number | boolean>;
const person: Person = {
name: "Echo",
age: 26,
gender: "Male",
bool: true,
}
5. 总结
以上就是我对 TypeScript 中索引签名的理解,如果有不正确的地方,欢迎大家在评论区多多指正!
看完记得点个赞哦~ 谢谢!🤞🤞🤞