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

'return' statement required after function that returns 'never' #10470

Closed
jovdb opened this issue Aug 22, 2016 · 10 comments
Closed

'return' statement required after function that returns 'never' #10470

jovdb opened this issue Aug 22, 2016 · 10 comments
Labels
Design Limitation Constraints of the existing architecture prevent this from being fixed Question An issue which isn't directly actionable in code

Comments

@jovdb
Copy link

jovdb commented Aug 22, 2016

TypeScript Version: 2.1.0-dev.20160819

Code

// Type representing the falsy values
type Falsy = null | undefined | false | 0 | ''; // No NaN type

function assert(value: Falsy, message: string): never;
function assert<T>(value: T, message: string): T;
function assert<T>(value: Falsy | T, message: string): never | T {
  if (!value) throw new Error(`AssertError: ${message}`);
  return value;
}

function foo(bar: number): string {

  // Unimportant
  if (bar === 0) return 'off';
  if (bar > 1) return 'on';

  assert(false, 'Invalid value for bar'); // because of 'false' argument, this will have return type 'never'

  // Error: Function lacks ending return statement and return type does not include 'undefined'
}

Expected behavior:
I would expect if the type system knows that assert(false, '') never returns, it doesn't require a return statement after assert.

Actual behavior:
Error on foo function:
Function lacks ending return statement and return type does not include 'undefined'

@yortus
Copy link
Contributor

yortus commented Aug 22, 2016

Somewhat related: #8655

@kitsonk
Copy link
Contributor

kitsonk commented Aug 22, 2016

A simple fix is:

function foo(bar: number): string | undefined {

  if (bar = 0) return 'off';
  if (bar > 1) return 'on';

  assert(false, 'Invalid value for bar');
}

It does appear to essentially be a dupe of #8655 in the sense that this is valid:

function foo(bar: number): string {

  // Unimportant
  if (bar = 0) return 'off';
  if (bar > 1) return 'on';

  throw new Error('Invalid value for bar');
}

On the other hand, never says that there will not be a return, it does not contract to say that it will throw, which is what the code analysis is trying to determine.

@jovdb
Copy link
Author

jovdb commented Aug 22, 2016

@kitsonk
The problem is not related to the Error thrown. If the assert function is a function with an infinite loop:

function assert(): never {
    while (true) {
    }
}

the problem would be the same.
See: https://github.com/Microsoft/TypeScript/wiki/What's-new-in-TypeScript#the-never-type

The suggested solution adds | undefined to the signature of the function and requires unneeded null checks after calling the foo() function.

To satisfy the compiler, I think I'm better of with adding just a return statement:

function foo(bar: number): string {

  if (bar === 0) return 'off';
  if (bar > 1) return 'on';

  assert(false, 'Invalid value for bar'); // because of 'false' argument, this will have return type 'never'
  return undefined!; // never reached
}

@kitsonk
Copy link
Contributor

kitsonk commented Aug 22, 2016

I totally understand what the never type is. It is a bottom type, but it does not affect the flow control of the code, for some very important reasons. In fact, the "what is new" expresses the behaviour you are experiencing is by design:

// Function returning never must have unreachable end point
function error(message: string): never {
    throw new Error(message);
}

// Inferred return type is number
function move1(direction: "up" | "down") {
    switch (direction) {
        case "up":
            return 1;
        case "down":
            return -1; 
    }
    return error("Should never get here");
}

// Inferred return type is number
function move2(direction: "up" | "down") {
    return direction === "up" ? 1 :
        direction === "down" ? -1 :
        error("Should never get here");
}

// Inferred return type is T
function check<T>(x: T | undefined) {
    return x || error("Undefined value");
}

And to quote:

Because never is a subtype of every type, it is always omitted from union types and it is ignored in function return type inference as long as there are other types being returned.

@jovdb
Copy link
Author

jovdb commented Aug 22, 2016

The 'problem' I have is when there is code after a function that never returns.

The compiler warns that there is a code path that doesn't return a value.
I think the compiler warning is not needed there, because that code path will/can never be reached (at least if the function type signature doesn't lie)

@RyanCavanaugh
Copy link
Member

The intended fix is

return assert(false, 'Invalid value for bar');

There are technical reasons why we can't figure this out correctly today but I'm not remembering exactly why right now. I'll follow up.

@jovdb
Copy link
Author

jovdb commented Aug 22, 2016

Thanx

@mhegazy mhegazy added Question An issue which isn't directly actionable in code Design Limitation Constraints of the existing architecture prevent this from being fixed labels Aug 22, 2016
@mhegazy mhegazy closed this as completed Aug 22, 2016
@samverneck
Copy link

How can I resolve these errors here?

[ts] Function lacks ending return statement and return type does not include 'undefined'.
[ts] Parameter 'conversation' implicitly has an 'any' type.
[ts] Not all code paths return a value.

@kitsonk
Copy link
Contributor

kitsonk commented Jul 25, 2017

By asking "how to use TypeScript" questions on StackOverflow or Gitter?

@RyanCavanaugh
Copy link
Member

@kitsonk 💯

Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
Design Limitation Constraints of the existing architecture prevent this from being fixed Question An issue which isn't directly actionable in code
Projects
None yet
Development

No branches or pull requests

6 participants