Skip to content

Erroneous type narrowing in finally #18478

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
Zbyl opened this issue Sep 14, 2017 · 5 comments
Closed

Erroneous type narrowing in finally #18478

Zbyl opened this issue Sep 14, 2017 · 5 comments
Assignees
Labels
Bug A bug in TypeScript Fixed A PR has been merged for this issue
Milestone

Comments

@Zbyl
Copy link

Zbyl commented Sep 14, 2017

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": #15835

@ghost
Copy link

ghost commented Nov 13, 2017

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;
    }
}

@mhegazy mhegazy added this to the TypeScript 2.9 milestone Apr 12, 2018
@mhegazy mhegazy modified the milestones: TypeScript 3.0, Future Jul 2, 2018
@rdsedmundo
Copy link

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();
    }
  }

@dtiziani
Copy link

dtiziani commented Dec 12, 2019

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)
    }
  }

@sandersn sandersn removed their assignment Jan 7, 2020
@SlurpTheo
Copy link

I can't reproduce the earlier problems in this issue anymore (using typescript@3.7.4), 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.

@RyanCavanaugh
Copy link
Member

@SlurpTheo fixed in nightly; see #34880

@RyanCavanaugh RyanCavanaugh added the Fixed A PR has been merged for this issue label Jan 9, 2020
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Bug A bug in TypeScript Fixed A PR has been merged for this issue
Projects
None yet
Development

No branches or pull requests

8 participants