为何固定类型可赋值给带索引签名的类型,而接口却不行?
TypeScript中type与interface在索引签名类型赋值上的差异
在TypeScript里会遇到一个奇怪的现象:用type定义的固定结构类型可以赋值给带有索引签名的类型,但用interface定义的接口却不行。示例代码如下:
interface JsonObject { [key: string]: JsonValue } type JsonValue = JsonObject | JsonValue[] | boolean | number | string | null; export type Json = JsonObject | JsonValue; //--------------------------------------------------------------- interface A { b: string } type B = { b: string }; const AValue: A = { b: "foo" }; const BValue: B = { b: "foo" }; function serialize(val: Json) { JSON.stringify(val); } serialize(AValue); //TS2345错误 serialize(BValue); //无错误 // 以下两行均会报错 AValue["x"] = 4; BValue["x"] = 4;
核心原因:兼容性检查逻辑的差异
type定义的对象类型:结构兼容优先
TypeScript对type定义的对象类型采用宽松的结构类型检查:只要目标类型(如JsonObject)的索引签名要求,能覆盖源类型(B)的所有已知属性类型,就判定两者兼容。
示例中JsonObject要求所有字符串键的属性都属于JsonValue类型,而type B的b属性是string,正好是JsonValue的子集,所以B的实例可以被当作JsonObject传入serialize函数。interface定义的类型:精确结构默认不兼容索引签名
对于interface,TypeScript默认将其视为精确的结构声明——它认为接口A只包含b这一个属性,没有声明索引签名,因此不会自动推断它符合JsonObject的约束。如果想让A兼容JsonObject,需要给接口显式添加对应的索引签名:interface A { b: string; [key: string]: JsonValue; }修改后,
AValue就能正常传入serialize函数了。
关于无法添加额外属性的说明
不管是interface A还是type B,它们都没有声明索引签名,所以编译时TypeScript会阻止添加未定义的属性(比如x),这是两者一致的行为,和前面的赋值兼容性问题是独立的逻辑。
内容的提问来源于stack exchange,提问作者Chris
相关产品推荐
相关产品推荐

