Record<string, any> 和 {} 之间的区别是什么?

6

我看到的唯一区别是你不能向{}添加额外属性,但可以向Record<string, any>添加。还有其他我没注意到的吗?

示例代码(playground link):

const one: Record<string, any> = { foo: 1 }
one.bar = 2

const two: {} = { foo: 1 }
two.bar = 2 // Property 'bar' does not exist on type '{}'.(2339)
2个回答

8

好的,所以真正的问题是为什么我们可以将任何对象分配给 {}。这更多关于语言设计选择。语言是基于结构化子类型兼容性建立起来的。

TypeScript 中的类型兼容性基于结构化子类型。结构化类型是一种仅根据成员来关联类型的方式。这与名义类型相反。

这意味着我们可以将其子类型的值赋给更松散的类型。在你的代码中,{} 是最宽泛的对象类型,我们可以说每个对象类型的父类型都是它,换句话说,每个其他对象类型都是 {} 的子类型。以下是一些示例检查:

type ISubTypeOfEmptyObj<T> = T extends {} ? true : false
type Check1 = ISubTypeOfEmptyObj<{bar: 2}> // yes it is
type Check2 = ISubTypeOfEmptyObj<{foo: 2, bar: 'a'}> // yes it is
type Check3 = ISubTypeOfEmptyObj<number> // surprisingly that is also true
const a: {} = 1; // no error 

这证明了什么 - 事实上你可以从几乎任何东西中创建一个 {} 的实例 (只有 nullundefined 值不能成为 {} 的成员)。但同样也是正确的,初始化之后你不能将某些属性设置为这样的值,因为类型表明没有属性。换句话说,你可以从几乎任何东西中创建 {},但你只能像空对象一样使用它。

Record<string, any>{} 更严格,例如每个数组类型都可以分配给 {},但无法分配给 Record<string, any>,对于其他类型如stringnumber也是如此。 Record<string, any> 表示具有任何字符串属性的所有对象,因此可以添加任何字符串属性到该对象,类型会保持一致。


那么为什么const two: {} = { foo: 1 }不会抛出错误呢? - maxedison
我希望我已经回答了你的问题。 - Maciej Sikora
谢谢,你给出的额外示例使这更加奇怪!有没有一个好的用例可以将某些东西键入为{} - maxedison
我认为这种类型并没有必要。即使在 TS 的常见问题解答中也提到了你永远不需要它。 - Maciej Sikora

7

类型{}只匹配空对象。

Record<string, any>类似于{ [key: string]: any}

更新: 我的第一句话是错误的。正如接受的答案所说,类型{}可以匹配几乎任何东西。


那么为什么 const two: {} = { foo: 1 } 不会抛出错误呢? - maxedison
1
不对,第一部分是错误的。{} 是可能的最宽泛的类型之一,而不仅仅是一个空对象字面量类型。今天 {} 不再那么有用了,因为你最好可以使用 unknown - ford04

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接