为什么返回类型“null"(或任何其他类型)可以分配给返回类型“void"? [英] why is return type `null` (or any other type) assignable to return type `void`?

查看:31
本文介绍了为什么返回类型“null"(或任何其他类型)可以分配给返回类型“void"?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

您可能知道,在严格模式下,只有 undefined 可以分配给 void 类型.因此,如果您尝试:

声明 let _void: void;_void = 空;//错误_void = 5;//错误

你会得到错误

<块引用>

[type] 不能分配给 void 类型.

但是如果您尝试将其作为返回类型,则一切正常:

声明 let voidReturn: () =>空白;声明 let nullReturn: () =>空值;声明 let numReturn: () =>数字;voidReturn = numReturn;voidReturn = nullReturn;

void 在这里的行为类似于 any,因为它可以容纳任何类型(但它只能以一种方式工作 - 将 void 分配给任何其他返回类型引发错误).

这是为什么?这是错误还是功能?

解决方案

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


这是预期的行为,而不是错误.TypeScript 的 void 类型通常应该表示不可用的东西,不一定不存在.

一方面,编译器假定您有意并显式地将任何非未定义类型的值分配给变量或属性 类型为 void.

另一方面,它将返回类型为void的函数视为调用者不能安全地使用此函数的返回值",而不是此函数肯定会返回未定义."因此,它允许您在任何需要 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> 承诺:

接口数组{pedanticForEach(cb: (val: T) => undefined): void;}Array.prototype.pedanticForEach = Array.prototype.forEach;arr1.pedanticForEach(v => arr2.push(v));//错误!arr1.pedanticForEach(v => (arr2.push(v), undefined));//好的arr1.pedanticForEach(v => void arr2.push(v));//好的

但是使用 void 值本身的类似操作用处不大.没有一个常见的用例是有人真的想将 number 值显式分配给 void 类型的变量.可能是你做的时候出错了.

为了使其一致,他们要么必须允许这种可能的错误,要么强迫人们将他们的非实际void 返回回调函数与明确的void-ish.其中任何一个都会损害生产力.

因此,在这种情况下,实用性和开发人员生产力胜过稳健性和一致性.这种权衡在语言中很常见;严格的稳健性和类型安全性不是 TypeScript 的设计目标之一.事实上,TypeScript Design -目标 #3 是

<块引用>

应用声音或可证明是正确的";类型系统.相反,在正确性和生产力之间取得平衡.


游乐场链接到代码

As you may know, in strict mode only undefined is assignable to type void. So if you try:

declare let _void: void;
_void = null; // error
_void = 5; // error

you'll get errors

[type] is not assignable to type void.

But if you try this as a return type, everything is okay:

declare let voidReturn: () => void;
declare let nullReturn: () => null;
declare let numReturn: () => number;

voidReturn = numReturn;
voidReturn = nullReturn;

void behaves here like any since it can accomodate any type (but it only works in one way - assigning void to any other return type rises an error).

Why is that? Is this a bug or a feature?

解决方案

Canonical answers to this question can be found in:


It is the intended behavior, not a bug. TypeScript's void type is usually supposed to represent something unusable, not necessarily absent.

On the one hand, the compiler assumes that it is probably an error for you to intentionally and explicitly assign a value of any non-undefined type to a variable or property of type void.

On the other hand, it treats a function whose return type of void to mean "callers cannot safely use the return value of this function", not "this function will definitely return undefined." Therefore it lets you assign a non-void-returning function value anywhere a void-returning function is expected, since the caller would never be checking the return value anyway.

These two situations are, on the face of it, inconsistent; generally speaking, due to covariance of return types, the type ()=>A is assignable to ()=>B if and only if A is assignable to B. This breaks down with void, and is presumably why you are bothered by the situation.


But despite being inconsistent it is, for better or worse, intentional. The reason is that it is incredibly useful to be able to ignore callback return values, especially for arrow functions which happen to have side effects and return values. The go-to example here is Array.prototype.push(), which mutates the array you call it on and returns its new length. I want to call

const arr1 = [4, 5, 6];
const arr2 = [1, 2, 3];
arr1.forEach(v => arr2.push(v))

without having forEach() get angry at me because push() returns a number instead of the void it was promised:

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

But the analogous operation with void values themselves is much less useful. There is no common use case in which someone really wants to assign a number value explicitly to a variable of type void. It's probably an error when you do it.

In order to make it consistent, they'd either have to allow this probable error, or force people to wrap their not-actually-void returning callback functions with something explicitly void-ish. Either of which would hurt productivity.

So usefulness and developer productivity win over soundness and consistency in this case. Such trade-offs are common in the language; strict soundness and type safety is not one of TypeScript's design goals. In fact, TypeScript Design Non-Goal #3 is to

Apply a sound or "provably correct" type system. Instead, strike a balance between correctness and productivity.


Playground link to code

这篇关于为什么返回类型“null"(或任何其他类型)可以分配给返回类型“void"?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
相关文章
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆