为什么某些基本类型在 TypeScript 中不能传递赋值——它们是否按预期运行?

2024-04-05

(本来想在 TypeScript 存储库的问题跟踪器中打开一个错误报告,我意识到我问了太多问题,所以我在错误报告之前在这里打开一个问题。)

带有相关代码的 Playground 链接 https://www.typescriptlang.org/play?ts=4.8.0-beta#code/C4TwDgpgBAYg9nADFAvFA3gXyhAHsCAOwBMBnKAJQgGM4AnYgHlODoEtCBzAGigFdCAa0JwA7oQB8UAPxRWfaAC4oAMwCGAG1IQA3ACgA9AagmAetL2hIsBAEZUlGvSYt2XXgOFjJOfETIY2LLySqqa2vpGJlDmepbg0ABCanTIaFi+BCTkcABGAFY0wDJydApQyupauobGZhZWSSn2aHmF1MV4WQEZwWWhVRG10bHx1skAXmmOtAzMrBw8-EIi4lJd-jkFRSUhFWHVkXUxDQlQky1QbTsb2TPO825Lnqs+feWV4TVR9XpAA

type Foo0 = {} extends Record<string, unknown> ? true : false;
//   ^? [type Foo0 = true]
type Foo1 = Record<string, unknown> extends {} ? true : false;
//   ^? [type Foo1 = true]

type Bar0 = {} extends object ? true : false;
//   ^? [type Bar0 = true]
type Bar1 = object extends {} ? true : false;
//   ^? [type Bar1 = true]

type Baz0 = Record<string, unknown> extends object ? true : false;
//   ^? [type Baz0 = true]
type Baz1 = object extends Record<string, unknown> ? true : false;
//   ^? [type Baz1 = false]

编辑的问题:我想我已经明白了。在Foo0 and Bar0, {}表示“一个空对象(没有任何值的记录)”,因此它可以分配给两者object(代表“任何非原始”)和Record<string, unknown>(这相当于{ [key: string]: unknown })。另一方面,{} in Foo1 and Bar1代表“任何非空值”,因此两者object and Record<string, unknown>可以分配给它。所以我想我的问题会变成:

Why {}代表两种不同的事物?是故意的吗?如果是因为历史原因,这两件事有没有可能分开呢?

如果我做错了什么,请指出他们。


我对 TS 的理解还不足以真正解决这个问题,但我会根据我设法查找的内容提供一些观察结果。

首先:object是任何非原始的。所以,例如。函数是objects 和字符串不是:

let bar1: object = alert // typechecks
let bar2: object = 'a' // doesn't

的特殊属性{}另一方面,“可以从任何非空/未定义值分配”:https://gist.github.com/OliverJAsh/381cd397008309c4a95d8f9bd31adcd7?permalink_comment_id=2890808#gistcomment-2890808 https://gist.github.com/OliverJAsh/381cd397008309c4a95d8f9bd31adcd7?permalink_comment_id=2890808#gistcomment-2890808

{} 是空类型,可以从任何非 null/非未定义类型分配。请记住此处的结构类型:减少类型中的属性数量(不应该)永远不会减少可分配给该类型的类型数量(注意,过多的属性检查不是可分配性的因素;它是单独的检查)。

请注意这里一些有问题的对称性:字符串应该可分配给 { length: number },并且对于任何类型 { r0; r1; r2...},该类型的可分配值也应可分配给 { r1; r2...}。因此,如果 string 可分配给 { length: number } (这绝对应该是),那么 string 也必须可分配给 { },即使它不是一个对象。

所以这是之间的区别{} and object,如图所示:

let bar0: {} = 'a' // typechecks
let bar1: object = 'a' // doesn't

这意味着一个明显可疑的部分是{} extends object:

let bar0: {} = 'a'
let bar1: object = {}
bar1 = bar0 // shouldn't work but it does

https://github.com/microsoft/TypeScript/pull/49119 https://github.com/microsoft/TypeScript/pull/49119是关于另一个方向——事物的可分配性{}。我搜索过但没有找到任何有关分配的信息{}对事物。

我不确定 TS 是否真的关心维护原语(例如string) 不能以object。如果它确实关心,那么正如您提到的,应该有一个false在前两对中的每一对中。

但是没问题。假设它不在乎。

第二个不确定的部分是以下之间的差异:

{}     extends Record<string, unknown> = true
object extends Record<string, unknown> = false

TypeScript 索引签名的实际含义是什么? https://stackoverflow.com/questions/58458308/what-does-a-typescript-index-signature-actually-mean可能会提供有关/为什么第二个是的线索false.

我花了最后一个小时试图理解索引签名(由Record)并且我无法为它们想出一个有意义的清晰语义。

也许唯一好的解释是“如果很明显您将要注入错误键入的键/值,TS 想要抱怨,但否则它不会抗议太多”。所以object是一种垃圾堆,不应该最终进入{[key: string]: unknown}, but {}很好,尽管它可能绝对也是垃圾堆。

我希望 TS 类型检查规则实际上写在某个地方。

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

为什么某些基本类型在 TypeScript 中不能传递赋值——它们是否按预期运行? 的相关文章

随机推荐