我看到的唯一区别是你不能向{}
添加额外属性,但可以向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)
我看到的唯一区别是你不能向{}
添加额外属性,但可以向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)
好的,所以真正的问题是为什么我们可以将任何对象分配给 {}
。这更多关于语言设计选择。语言是基于结构化子类型兼容性建立起来的。
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
这证明了什么 - 事实上你可以从几乎任何东西中创建一个 {}
的实例 (只有 null
和 undefined
值不能成为 {}
的成员)。但同样也是正确的,初始化之后你不能将某些属性设置为这样的值,因为类型表明没有属性。换句话说,你可以从几乎任何东西中创建 {}
,但你只能像空对象一样使用它。
Record<string, any>
比 {}
更严格,例如每个数组类型都可以分配给 {}
,但无法分配给 Record<string, any>
,对于其他类型如string
或number
也是如此。 Record<string, any>
表示具有任何字符串属性的所有对象,因此可以添加任何字符串属性到该对象,类型会保持一致。
类型{}
只匹配空对象。
Record<string, any>
类似于{ [key: string]: any}
更新:
我的第一句话是错误的。正如接受的答案所说,类型{}
可以匹配几乎任何东西。
const two: {} = { foo: 1 }
不会抛出错误呢? - maxedison{}
是可能的最宽泛的类型之一,而不仅仅是一个空对象字面量类型。今天 {}
不再那么有用了,因为你最好可以使用 unknown
。 - ford04
const two: {} = { foo: 1 }
不会抛出错误呢? - maxedison{}
? - maxedison