为什么返回类型“null”(或任何其他类型)可以分配给返回类型“void”?

问题描述 投票:0回答:1

您可能知道,在严格模式下,只有

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
1个回答
8
投票

此问题的规范答案可以在以下位置找到:


这是预期的行为,而不是错误。 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 代码链接

© www.soinside.com 2019 - 2024. All rights reserved.