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
~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.
@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
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();?
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)
})()
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?
@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
Duplicate of #12825
Most helpful comment
@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
neveras if an error was thrown.A better analogue would be: