为何Record<string, any>会出现指定键“缺失”的类型兼容问题?
先看这段代码:
// 合法 let a: Record<string, any> = {data: "hello"}; // 类型“Record<string, any>”中缺少属性“data”,但类型“{ data: string; }”中需要该属性 let b: {data: string} = a;
为什么会出现这个报错?Record<string, any>的设计不就是指定键类型为string、值类型为any吗?为什么断言该记录存在特定键会不兼容?
另外我知道把属性设为可选就合法:
// 合法 let b: {data?: string} = a;
为什么像{data: any}这种更具体的类型,不能和Record<string, any>兼容?
核心原因:类型语义的差异
Record<string, any>的本质
它表示:所有字符串类型的键都允许存在,对应的值可以是任意类型,但不保证任何特定键一定存在。换句话说,Record<string, any>的实例可以是{}、{name: 123}、{data: "hello", age: 20}等任意键为string的对象,甚至之后可以给它赋值成完全空的对象,这都符合类型要求。{data: string}的语义
这个类型要求:对象必须包含data属性,且属性值是string类型。如果把a(Record<string, any>类型)赋值给b,TypeScript无法保证a一定有data键——比如之后你执行a = {},那b.data就会变成undefined,违背了{data: string}必须存在该属性的约定,所以类型检查会报错。可选属性为什么兼容
{data?: string}表示data属性可以存在也可以不存在,这和Record<string, any>的语义完全匹配:Record<string, any>既允许有data键,也允许没有,所以赋值不会有问题。为什么
{data: any}不能反向兼容Record<string, any>
类型兼容是从更具体到更宽泛的方向:你可以把{data: any}赋值给Record<string, any>(因为前者满足后者“键是string、值是any”的要求),但反过来不行——Record<string, any>的实例可能没有data键,而{data: any}要求必须有这个键,两者的约束方向相反,所以不兼容。
内容的提问来源于stack exchange,提问作者joshstrike

