Welcome to WuJiGu Developer Q&A Community for programmer and developer-Open, Learning and Share
Welcome To Ask or Share your Answers For Others

Categories

0 votes
813 views
in Technique[技术] by (71.8m points)

typescript - why is return type `null` (or any other type) assignable to return type `void`?

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?

See Question&Answers more detail:os

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome To Ask or Share your Answers For Others

1 Answer

0 votes
by (71.8m points)

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


与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome to WuJiGu Developer Q&A Community for programmer and developer-Open, Learning and Share
...