Skip to content

Type inference and "never" returning function in strict mode #20933

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

Closed
arusakov opened this issue Dec 29, 2017 · 7 comments
Closed

Type inference and "never" returning function in strict mode #20933

arusakov opened this issue Dec 29, 2017 · 7 comments
Labels
Duplicate An existing issue was already created

Comments

@arusakov
Copy link
Contributor

TypeScript Version: 2.7.0-dev.20171229

Code

// tsc --strict --noEmit test.ts

declare const throwError: () => never
declare const obj: { prop?: number }

if (!obj.prop) {
  throwError()
}
obj.prop.toFixed(1) // error TS2532: Object is possibly 'undefined'.

Expected behavior:
At the last line obj.prop is number, not undefined

Actual behavior:
At the last line obj.prop is possibly undefined

@arusakov arusakov changed the title Type inference and "never" returning function Type inference and "never" returning function in strict mode Dec 29, 2017
@jack-williams
Copy link
Collaborator

jack-williams commented Dec 29, 2017

Not sure this is related to the never returning function, or the function at all. I get the same with

// tsc --strictNullCheckts --noEmit test.ts
declare const obj: { prop?: number }
if (!obj.prop) {
}
obj.prop.toFixed(1) // error TS2532: Object is possibly 'undefined'.

Edit: Don't think my comment above is relevant, but I won't delete it and leave a dangling reference from the post below.

@kitsonk
Copy link
Contributor

kitsonk commented Dec 29, 2017

@jack-williams that would be expected behaviour, because the last line is reachable code. The issue relates to code that is not reachable.

@arusakov is suggesting that CFA should treat a function that returns never as if an error was thrown.

A better analogue would be:

// tsc --strictNullCheckts --noEmit test.ts
declare const obj: { prop?: number }
if (!obj.prop) {
    throw Error('Missing property');
}
obj.prop.toFixed(1); // no error, because this is not reachable

@jack-williams
Copy link
Collaborator

jack-williams commented Dec 29, 2017

You're correct @kitsonk that I misread.

On the actual topic, is it safe to assume that constructing a value of type never always implies divergence?

Am I correct in saything that this example only really applies to top-level code where you can't just write return throwError();?

@kitsonk
Copy link
Contributor

kitsonk commented Dec 29, 2017

I believe never is always intended to be a bottom type and nothing more. Used in situations where either there cannot possibly be a value, or where TypeScript cannot possible infer a value type (e.g. const arr = []; in strict mode).

As you point out, it can easily be worked around by returning from a function:

// tsc --strict --noEmit test.ts

declare const throwError: () => never
declare const obj: { prop?: number }

(() => {
  if (!obj.prop) {
    return throwError()
  }
  obj.prop.toFixed(1)
})()

@jack-williams
Copy link
Collaborator

I understand that never is meant to be like bottom, but does bottom needs some defined semantics with statement composition (;)? If there are two statements S1 and S2, composed as S1;S2, and S1 has type never, then should S2 not be unreachable as it's predicated on S1 delivering a value?

@arusakov
Copy link
Contributor Author

@kitsonk
Very good example with throw new Error(). More real code looks like that:

function throwError() {
  throw new Error()
}

declare const obj: { prop?: number }

if (!obj.prop) {
  throwError()
}
obj.prop.toFixed(1) // error TS2532: Object is possibly 'undefined'.

Example in real life: ctx.throw() method from koa framework for node.js

@RyanCavanaugh RyanCavanaugh added the Duplicate An existing issue was already created label Dec 29, 2017
@RyanCavanaugh
Copy link
Member

Duplicate of #12825

@RyanCavanaugh RyanCavanaugh marked this as a duplicate of #12825 Dec 29, 2017
@arusakov arusakov closed this as completed Jan 9, 2018
@microsoft microsoft locked and limited conversation to collaborators Jul 3, 2018
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
Duplicate An existing issue was already created
Projects
None yet
Development

No branches or pull requests

4 participants