Typescript: Erroneous type narrowing in finally

Created on 14 Sep 2017  路  5Comments  路  Source: microsoft/TypeScript

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

Bug Fixed

Most helpful comment

@SlurpTheo fixed in nightly; see #34880

All 5 comments

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

Was this page helpful?
0 / 5 - 0 ratings