您可能知道,在严格模式下,只有
undefined
可以分配给类型 void
。所以如果你尝试:
declare let _void: void;
_void = null; // error
_void = 5; // error
你会得到错误
[type] 不可分配给 void 类型。
但是如果你尝试将其作为返回类型,一切都很好:
declare let voidReturn: () => void;
declare let nullReturn: () => null;
declare let numReturn: () => number;
voidReturn = numReturn;
voidReturn = nullReturn;
void
在这里的行为与 any
类似,因为它可以容纳任何类型(但它只能以一种方式工作 - 将 void
分配给任何其他返回类型会引发错误)。
这是为什么呢?这是错误还是功能?
此问题的规范答案可以在以下位置找到:
TypeScript 常见问题解答条目“为什么返回非
void
的函数可分配给返回 void
的函数?”
() => void
不是() => a
的子类型并且void
不是a
的子类型,那么为什么a
是void
的子类型?”
microsoft/TypeScript#20006“[文档]澄清 void 的语义”
还有许多其他的,如 microsoft/TypeScript #8240、#8584、#8615、#9603、#19014等...
这是预期的行为,而不是错误。 TypeScript 的
void
类型通常应该代表某些不可用,而不一定是不存在。
一方面,编译器认为您有意且显式地将任何非
undefined
类型的值分配给 void
类型的 变量或属性可能是错误的。
另一方面,它将返回类型为
void
的函数视为“调用者无法安全地使用该函数的返回值”,而不是“该函数肯定会返回 undefined
”。 因此,它允许您在任何需要 void
返回函数的地方分配一个非 void
返回函数值,因为调用者无论如何都不会检查返回值。
这两种情况表面上看是不一致的;一般来说,由于返回类型的协变性,当且仅当()=>A
可分配给
()=>B
时,类型
A
才可分配给
B
。 这与
void
不同,这可能就是您对这种情况感到困扰的原因。
Array.prototype.push()
,它会改变您调用它的数组并返回其新长度。 我要打电话
const arr1 = [4, 5, 6];
const arr2 = [1, 2, 3];
arr1.forEach(v => arr2.push(v))
没有 forEach()
对我生气,因为 push()
返回一个
number
而不是承诺的
void
:
interface Array<T> {
pedanticForEach(cb: (val: T) => undefined): void;
}
Array.prototype.pedanticForEach = Array.prototype.forEach;
arr1.pedanticForEach(v => arr2.push(v)); // error!
arr1.pedanticForEach(v => (arr2.push(v), undefined)); // okay
arr1.pedanticForEach(v => void arr2.push(v)); // okay
但是与 void
值本身的类似操作没那么有用
。 不存在有人真正想要将
number
值显式分配给 void
类型的变量的常见用例。 当你这样做时可能会出错。
为了使其保持一致,他们要么必须允许这种可能的错误,要么强迫人们用明确的
void
风格的东西包装他们的非实际的 void
返回回调函数。 其中任何一个都会损害生产力。
因此,在这种情况下,实用性和开发人员生产力胜过健全性和一致性。这种权衡在语言中很常见。严格的健全性和类型安全并不是 TypeScript 的设计目标之一。 事实上,TypeScript 设计非
-目标#3 是 应用健全的或“可证明正确的”类型的系统。相反,要在正确性和生产力之间取得平衡。
Playground 代码链接