索引签名(Index Signature)是 TypeScript 中用来描述对象属性动态访问方式的一种类型注解。简单来说,当你不确定对象会包含哪些具体的属性名,但知道所有属性的键的类型和值的类型时,就可以通过索引签名来提前声明。这种注解方式最常用于定义字典、映射表、配置对象或缓存容器等结构。

索引签名的基本语法
索引签名在接口(interface)或类型别名(type)中定义,其形式为:
interface Dictionary {
[key: string]: string;
}这里的 [key: string]: string 就是一个索引签名。key 是键名占位符(可以随意命名,如 index、prop),string 是键的类型(通常为 string、number 或 symbol),后面的 string 是值的类型。这段代码表示:Dictionary 类型的对象,其所有属性名都是字符串类型,且对应的属性值也必须为字符串。
注解书写方式详解
1. 接口中使用索引签名
最常见的方式就是在 interface 中直接写出索引签名:
interface StringMap {
[key: string]: string;
}
let config: StringMap = {
apiUrl: "https://ipipp.com/api",
timeout: "5000" // 必须为 string,如果写成 5000 会报错
};2. 类型别名中使用索引签名
类型别名的写法与接口类似:
type NumberKeyed = {
[index: number]: boolean;
};
let flags: NumberKeyed = {
0: true,
1: false
};3. 联合键类型与只读索引签名
你还可以将键的类型声明为联合类型,或者使用 readonly 修饰符:
interface ReadonlyMap {
readonly [key: string]: number;
}
let scores: ReadonlyMap = {
math: 90,
english: 85
};
// scores.math = 95; // 错误,只读属性不能修改4. 可选属性与索引签名共存
索引签名允许对象有其他特定属性,但要注意这些特定属性的类型必须符合索引签名的值类型约束。例如:
interface UserData {
name: string; // 普通属性,类型为 string
age: number; // 普通属性,类型为 number
[key: string]: string | number; // 索引签名,值类型 = string | number
}这里 name 是 string,符合索引签名的值类型(string | number),age 是 number 也符合。但如果某个普通属性的类型返回了 boolean,就会导致编译错误,因为索引签名未包含 boolean。
5. 更精确的索引键类型
索引签名的键除了 string 和 number,还能使用模板字面量类型或 symbol:
type SymbolMap = {
[key: symbol]: string;
};
const sym = Symbol("id");
let map: SymbolMap = {
[sym]: "value"
};实际应用场景
场景一:API 响应数据映射
当后端返回的动态对象(如用户自定义字段)结构未知时,可以使用索引签名:
interface ApiResponse {
code: number;
data: {
[key: string]: any; // 允许任意属性,任意值
};
}
fetch("https://ipipp.com/api/user")
.then(res => res.json())
.then((json: ApiResponse) => {
console.log(json.data.username);
});场景二:简单字典缓存
实现一个键值为字符串的缓存容器:
interface Cache {
[key: string]: string;
}
function createCache(): Cache {
const store: Cache = {};
return {
get(key: string): string | undefined {
return store[key];
},
set(key: string, value: string): void {
store[key] = value;
}
};
}场景三:类型安全的枚举映射
需要将枚举值映射到对应处理函数时,索引签名可以确保所有枚举键都存在:
enum EventType {
Click = "click",
Hover = "hover"
}
type EventHandler = (data: any) => void;
interface EventHandlers {
[key in EventType]: EventHandler;
}
const handlers: EventHandlers = {
[EventType.Click]: (data) => console.log("Click", data),
[EventType.Hover]: (data) => console.log("Hover", data)
};注意事项与常见错误
1. 索引签名不能与后面定义的普通属性冲突
如果索引签名已声明所有键为 string,那么之后不能再添加一个键类型与索引签名不同的属性。例如:
interface Conflict {
[key: string]: string;
name: number; // 错误,name 的类型 number 与索引签名 string 不兼容
}正确的做法是让索引签名的值类型包含所有普通属性的值类型(用联合类型)。
2. 索引签名中的键名占位符
虽然 [key: string] 中的 key 名称可以任意,但建议使用有语义的名字,如 key、index、prop,以便他人理解。
3. 索引签名与 Record<K, V> 工具类型
TypeScript 内置了 Record 类型,本质上就是索引签名的简写:
// 以下两种写法几乎等价
type StringMap1 = { [key: string]: string };
type StringMap2 = Record<string, string>;对于常用的字符串键、固定值类型映射,推荐使用 Record 使代码更简洁。
4. 避免过度使用 any
虽然可以用 [key: string]: any 来绕过类型检查,但这会让 TypeScript 的保护能力大幅下降。在可能的情况下,尽量使用更具体的类型或联合类型。
总结
索引签名是 TypeScript 类型系统中非常实用的特性,它允许你以灵活的方式定义对象类型,同时保持类型安全。通过掌握 [key: string]: ValueType 的注解书写方式,结合只读、可选和联合类型,你可以轻松应对动态属性对象的场景。在实际开发中,优先考虑使用 Record 工具类型简化声明,并在需要精确控制时退回到完整的等号形式。理解索引签名与普通属性的共存规则,能帮助你写出更加健壮、自解释的 TypeScript 代码。
TypeScript索引签名类型注解接口对象类型修改时间:2026-06-08 16:54:34