这个问题的规范答案可以在以下位置找到:
-
TypeScript 常见问题解答条目对于“为什么函数返回非void
可分配给函数返回void
?"
-
微软/TypeScript#8581“为什么 () => void 是 () => a 的子类型,如果 void 不是 a 的子类型并且 a 不是 void 的子类型?”
-
微软/TypeScript#20006“[文档]澄清 void 的语义”
-
以及许多其他人,例如 microsoft/TypeScript#8240, #8584, #8615, #9603, #19014, etc...
这是预期的行为,而不是错误。 TypeScript 的void
类型通常应该代表某种东西unusable, 不必要absent.
一方面,编译器假设您有意且显式地分配任何非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 的设计目标之一。实际上,打字稿设计Non-Goal#3 是
应用健全的或“可证明正确的”类型系统。相反,要在正确性和生产力之间取得平衡。
Playground 代码链接