提问人:adal 提问时间:12/6/2022 最后编辑:adal 更新时间:12/7/2022 访问量:156
为什么基元类型(例如数字)可以分配给对象类型(例如数字),反之亦然?
why primitive types (eg. number) are assignable to object types(e.g. Number) but not vice versa?
问:
TLDR 版本:
为什么 TypeScript 允许以下内容
let num: Number = new Number(1);
num = 1;
但不允许这样做
let num: number = 1;
num = new Number(1)
众所周知,JS 有两个类似字符串、类似数字的值。
let primNum = 1;
let objNum = new Number(1);
let primString = "str";
let objString = new String("str");
建议大家不要混淆这些,并且非常喜欢原始版本。
TypeScript 是 Javascript 的静态类型超集,它试图最大限度地减少类型错误。如果存在可能会混淆的值,并且该操作会导致 flakey 结果,则 TS 会在编译时显示错误。例如,考虑 和 之间的区别。在 JavaScript 中,第一个值的计算结果为 ,第二个值的计算结果为 。
从 JS 的角度来看,您可以添加任意两个值。您可以毫无问题地添加两个字符串、两个数字、一个数字和一个字符串等。但是,这种行为令人困惑,当 的操作数不是字符串或两个数字时,TypeScript 通过拒绝编译来消除可能的混淆。重要的一点是,即使一些 JS 代码可能期望能够添加任何两个东西,或者这样的期望可能很少见,但没关系,TS 划清了界限,不允许这样的操作。1 + 1 + "1"
"1" + 1 + 1
21
111
+
然而,TypeScript 对以下几点毫无顾忌,
let num: Number = new Number(1);
num = 1;
首先,我们声明该变量为对象类型(在TypeScript术语a中),但稍后我们给该变量分配一个原始类型(在TS术语a中)。这是一个混淆,应该被 TS 发现,但 TS 允许它,即使没有警告。因此,人们可能会假设 TS 不区分 和 。(我们可能不喜欢不区分它们的决定,但至少在这种情况下,我们知道我们在做什么)num
Number
number
Number
number
但这也是不正确的。TS 正确地给出了以下错误:
let num: number = 1;
num = new Number(1)
这意味着可以将 a 分配给 a,但反之亦然?因此,主要问题是,如果 TS 可以区分 a 以及为什么它允许将 a 分配给 .为什么不禁止这两项任务?number
Number
number
Number
Number
number
这是一个错误吗?如果不是,这背后的设计决策是什么?
TS 目前没有规范,但旧的存档说明是这样说的
为了确定子类型、超类型和赋值兼容性关系,Number、Boolean 和 String 基元类型分别被视为具有与“Number”、“Boolean”和“String”接口相同的属性的对象类型。
如果这是真的,我们应该能够将 a 分配给 a(我们可以),但我们也应该能够做相反的事情。这意味着这个旧文档跟不上 TS 目前的工作方式。问题是确定基元和对象类型之间子类型关系的当前实际规则是什么。number
Number
人们可能不同意这样的规则,因为无论它是什么,它都不能消除 和 之间的混淆,但这不是重点。关键是,目前尚不清楚该规则是什么。number
Number
主要问题:确定基元类型何时是另一种类型的子类型的规则是什么?
在 StackOverflow 中提出了类似的问题,但所有答案都解释了数字基元类型和数字对象类型之间的区别,并建议避免使用对象版本。但这里的问题不在于 JavaScript 值,而在于 TypeScript 的类型系统。问题是,在什么基础上比较原始类型和对象类型?这个问题不是任何已经回答的问题的重复。
答:
重要提示:
不要使用构造函数。接下来假设你无视这个非常好的建议。Number
我想你问为什么允许这样做:
const bigN: Number = 123 // fine
如果不是:
const smallN: number = new Number() // error
你期望两者都是一个错误。
答案是,您可以将 a 用作 a,而不会注意到其中的区别。但是,如果不改变工作方式,您就不能将 a 用作 a。number
Number
Number
number
number
当作为构造函数(使用 )调用时,它会创建一个对象,该对象不是基元。例如,和(虽然是新的)。
Number
new
Number
typeof new Number(42) === "object"
new Number(42) !== 42
Number(42) == 42
警告:您很少会发现自己被用作构造函数。
Number
例如,请注意:
new Number(42) !== 42
所以:
const n1 = new Number(123)
const n2 = new Number(123)
if (n1 === n2) {
doSomethingImportant()
// expected this to run, never does.
}
根据运算符,人们期望两个相同的数字相等,但如果至少有一个是对象,那么情况就不是这样,你的代码可能会在某个地方中断。所以这是值得防范的。===
Number
另一方面,假设您确实期望:
const n1: Number = 123
const n2: Number = 123
if (n1 === n2) {
throw new Error('never expected this to happen!')
// expected this NOT to run, but it does
}
现在你可以争辩说这同样是错误的。但事情是这样的:
世界上检查数值等效性的代码量大大使期望两个 Number
对象不具有相同标识的代码量相形见绌。
这意味着这在天文数字上不太可能是一个错误。
这就是为什么 是 可分配给 .number
Number
就像 Typescript 中的许多东西一样,我认为这是纯粹和实用主义之间的权衡。绝大多数情况下,使用 a as 不会造成任何问题,而反过来则至少会破坏一些非常重要的行为,即预期的行为方式。number
Number
number
评论
number
Number
评论
int
Number
int
Number
Number
int
int
Number
number
Number
number
Number
一个
数字分配给一个数字
”?因为事实并非如此:typescriptlang.org/play?#code/......number
Number