TypeScript Version: Version 2.6.0-dev.20170914
Code
function test() {
type YesNo = ('No' | 'Yes');
let answer: YesNo = 'No'; // Bug disappears after adding 'as YesNo'.
try {
answer = 'Yes';
return; // Bug disappears after removing the return.
} finally {
if (answer === 'Yes') { // Bug: Operator === cannot be applied to types 'No' and 'Yes'
} else if (answer === 'No') {
}
}
}
Expected behavior:
No compilation errors.
Actual behavior:
Compilation error:
bug.ts(8,13): error TS2365: Operator '===' cannot be applied to types '"No"' and '"Yes"'.
Note:
This bug is similar to "Erroneous control flow type narrowing in loops": https://github.com/Microsoft/TypeScript/issues/15835
We should also handle catch correctly.
function f(x: boolean) {
let a: boolean = false;
try {
a = true;
if (x) throw new Error();
a = false;
}
catch (e) {
// this is possible, should be allowed to test
a === true;
}
}
Also facing this while setup a DB connection and trying to finalize it under the finally block if it was created.
let connection;
try {
connection = await getConnection();
const { rows } = await connection.query(sql, values);
return camelizeKeys(rows);
} finally {
if (connection) {
// [ts] Property 'end' does not exist on type 'never'. [2339]
await connection.end();
}
}
I'm facing the SAME issue, trying to finalize a db connection.
What I could figure out is that it happends when a function returns anything at all, it presents the error, but if it's a void function, the issue is not present.
Here's a playground with both scenarios: http://www.typescriptlang.org/play/?ssl=1&ssc=1&pln=29&pc=4#code/FAMwrgdgxgLglgewgAhHCATAQgTwHICGAtgKYAUExJAXMgM4wBO6A5gJS0BuCcGyA3sGTDkAGxIxkUJBBKxEEWg2YQWyAD7IIYUaOQBeLTtEBuYEJFMcAiyKky58JAa1UzdgL5SCMKAAtkMhI2Gzs7aQg6BHEAOlEEFiC2WzCYP0YEAHdkEncRLzRKXWtBMOQ4EDII2XlnAEJDbV0QiKjY+MTqxwU4klU05M8LD3NQSFqUQuwcAEkMMl4lJlYOemXVDSNdUJFxSS6JpZU1TSa9RuMzFKsd8IcJl14rsuFGCTBGFAOnCDzhLygPn8gWCtzCrWiJDiCSSKTsaQy2VyKQK6AIxTBwgqVXuP2QDS2ohaSDaUI6OIgNR+vX6fkG+WGQA
also the code below:
// No issues at all
function findByName(name: string): void {
let connection: string | null = null;
try {
connection = name;
} catch (e) {
console.log(e)
throw e;
} finally {
if(connection != null) console.log(connection.length)
}
}
// variable connection as never on finally block
function findById(id: string): string | null {
let connection: string | null = null;
try {
connection = id;
return connection;
} catch (e) {
console.log(e)
throw e;
} finally {
if(connection != null) console.log(connection.length)
}
}
I can't reproduce the earlier problems in this issue anymore (using [email protected]), but I _do_ get the same CFA problem described by @dtiziani in the findById()-example.
Commenting-out either the return or the throw statement prevents the problem.
Now, if you slightly tweak the example (to use undefined instead of null), there's another interesting bit that not explicitly initializing to = undefined prevents the problem as well:
function findById2(id: string) {
// not explicitly initializing this to `= undefined` prevents the problem
let connection: string | undefined = undefined;
try {
connection = id;
// commenting-out this `return` -OR- the `throw` below prevents the problem
return connection;
} catch (e) {
console.log(e);
// commenting-out this `throw` -OR- the `return` above prevents the problem
throw e;
} finally {
if (connection) {
// ts(2339) -
// Property 'length' does not exist on type 'never'.
console.log(connection.length);
}
}
}
And this "work-around" reminds me of this CFA conversation... only there's no function expression involved.
@SlurpTheo fixed in nightly; see #34880
Most helpful comment
@SlurpTheo fixed in nightly; see #34880