Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Arrow function allows return of different type with explicit void declaration as the return, normal function declarations though throws an error #37386

Closed
rdsedmundo opened this issue Mar 13, 2020 · 7 comments
Assignees
Labels
Working as Intended The behavior described is the intended behavior; this is not a bug

Comments

@rdsedmundo
Copy link

TypeScript Version: 3.8.3

Search Terms:
arrow function allows a return of different type with explicit void declaration

Code

const a = () => 3;

const f1: () => void = () => {
  // all good
  return a();
};

function f2(): void {
  // Type 'number' is not assignable to type 'void'. ts(2322)
  return a();
}

Expected behavior: both return statements should have the ts(2322) error.

Actual behavior: only the function declaration has the ts(2322) error, even though both functions have the same type.

Playground Link: http://www.typescriptlang.org/play/?ssl=1&ssc=1&pln=9&pc=2#code/MYewdgzgLgBAhjAvDAFASiQPhgZgNwBQBoksAZgIwBcqGi2AbiAJYAmStWMA3gTDACcAplACuAsPHSEAvoQJlRYYFGbgYZAEzoaTNjz6CR4yXGkEZQA

Related Issues: #20006

@RyanCavanaugh RyanCavanaugh added the Working as Intended The behavior described is the intended behavior; this is not a bug label Mar 13, 2020
@RyanCavanaugh
Copy link
Member

This is the intended behavior.

@rdsedmundo
Copy link
Author

Would you mind clarifying the reasoning? I found it strange considering that the final type of both functions is exactly the same: () => void. I tried to search about it in the documentation but didn't find anything. Thanks.

@AnyhowStep
Copy link
Contributor

AnyhowStep commented Mar 14, 2020

void means "there may or may not be a value here. Don't use it"

never means "There is no value here"

unknown means "there may or may not be a value here, but you may try and use it"

any means "there may or may not be a value here, but I just wanted to use it without TS type checking my code"

@rdsedmundo
Copy link
Author

That's all true @AnyhowStep, but it doesn't explain why in the function declaration it throws and in the arrow function it doesn't.

@AnyhowStep
Copy link
Contributor

AnyhowStep commented Mar 14, 2020

The same reason why we have excess property checks in some places, and don't in others.

A tradeoff between correctness, usability, and intent.

If you've explicitly annotated that you want a void return type, return /*number*/; is very suspicious and suggests the developer should take a step back and reevalaute that bit of code.

However, with () => void = () => number, the understanding is that the caller should not rely on the return type/value. So, this is technically safe.

Did the developer really mean to perform that assignment?
More likely than not. And this is where that correctness vs. usability thing comes in.

If the assignment was disallowed, now developers would have to force a cast (as) or wrap it in another function.

const a = () => 3;
//If this was not allowed,
const f: () => void = a;
//You'd need to always do this. Arguably bad.
const f1: () => void = () => {
  a();
};

@rdsedmundo
Copy link
Author

rdsedmundo commented Mar 14, 2020

In my opinion, this:

const f: () => void = a;

Should be disallowed yes, if a is a function that has a different return type. If you want this assignment to work, then you either use inference, or you use () => any and accept any function type. What I'm understanding here, in other words, is that declaring () => void is pointless.

But opinions aside in what's correct or not here, it's super strange that the function declaration behavior is different. It should've been at least consistent. For the user (developer) point of view, both are functions and behave exactly the same.

This definitely looks like related to #20006, as this behavior seems very opaque.

Look at what the docs are saying:

void is a little like the opposite of any: the absence of having any type at all. You may commonly see this as the return type of functions that do not return a value
https://www.typescriptlang.org/docs/handbook/basic-types.html#void

const f: () => void = a(); is a function, in the same manner that

function f(): void {
  return a();
}

is also.

@typescript-bot
Copy link
Collaborator

This issue has been marked 'Working as Intended' and has seen no recent activity. It has been automatically closed for house-keeping purposes.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Working as Intended The behavior described is the intended behavior; this is not a bug
Projects
None yet
Development

No branches or pull requests

5 participants