Skip to content

False positive from TS2801 (Promise<...> appears to always be defined) #43565

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
k-yle opened this issue Apr 7, 2021 · 7 comments
Closed

False positive from TS2801 (Promise<...> appears to always be defined) #43565

k-yle opened this issue Apr 7, 2021 · 7 comments

Comments

@k-yle
Copy link
Contributor

k-yle commented Apr 7, 2021

Bug Report

See code snippet below, if a type is a union with undefined it shouldn't throw the TS2801 error "This condition will always return true since this 'Promise' appears to always be defined."

Especially not when strictNullChecks: true

🔎 Search Terms

  • TS2801
  • Did you forget to use 'await'
  • This condition will always return true since this * appears to always be defined.

🕗 Version & Regression Information

v4.3.0-beta

  • This changed between versions 4.2.x and 4.3.x

⏯ Playground Link

Playground link, strictNullChecks is on

💻 Code

const x: Promise<number> = Promise.resolve(1);
const y: Promise<number> | undefined = x;

if (y) { // 🔴 this line should not error since y could be undefined so it's a valid check 
    // ...
}

🙁 Actual behaviour

"This condition will always return true since this 'Promise' appears to always be defined."

🙂 Expected behaviour

no error

@k-yle
Copy link
Contributor Author

k-yle commented Apr 7, 2021

I just saw @andrewbranch's comment on #43514

"The error goes away and everything works as expected if you use --noUncheckedIndexedAccess, or simply write the type annotation for thing:"

let thing: Promise<string> | undefined = cache[id];

That's what I've done here but there is still an error, in spite of explicitly adding | undefined

@MartinJohns
Copy link
Contributor

MartinJohns commented Apr 7, 2021

But TypeScript is right. The value y is always defined. The type of y at that point is Promise<number>, which can't be falsy.

@MartinJohns
Copy link
Contributor

sorry, ignore the above. I don't understand why typescript thinks y is always truthy but that seems like a bug:

It's not. You clearly assign a non-falsy value to your variable, so the variable is narrowed. This is the intentional behavior.

let x: number | undefined = 1;

is essentially the sama as

let x: number | undefined;
x = 1;

And you wouldn't expect an error to call a function on your number right away, would you?

let x: number | undefined = 1;
x.toFixed(); // Not error about potentially undefined, because TypeScript knows it's always defined.

here's a better playground example - in this example, y is always undefined at runtime, yet typescript claims it is always truthy. This is definitely wrong.

This is the same issue and absolutely intentional. w is always defined (according to the type system), so assigning this always defined value to y makes it always defined as well. Note that with any you lose all type safety.

@MartinJohns
Copy link
Contributor

You can get the behavior you want by using a type assertion:

let y: Promise<number> | undefined = x as Promise<number> | undefined;

Now TypeScript won't narrow y anymore, because you assign a potentially falsy type (Promise<number> | undefined).

@k-yle
Copy link
Contributor Author

k-yle commented Apr 7, 2021

@MartinJohns thanks for the detailed explanation, I deleted my comment, none of it made sense...

I guess this is a duplicate of #43514 then since the only real example arises when you access y from an index signature

@MartinJohns
Copy link
Contributor

At least the examples given here so far are not related to #43514.
They're intentional behavior due to the control flow based type analysis: #8010

let x: number | undefined
x = 1       // Assigning 1 makes clear that the type of x is number, not number | undefined,
x.toFixed() // so I can call methods of number without additional undefined-check.

@andrewbranch
Copy link
Member

The error goes away and everything works as expected if you use --noUncheckedIndexedAccess, or simply write the type annotation for thing

Yeah, this whole issue is my bad. I totally misspoke when I wrote this, and had intended to use as Promise<number> | undefined. The point was to demonstrate that the root of the problem in #43514 is that we were hitting a case where you knew the type of something might be undefined but the compiler didn’t, but I screwed up the example. Sorry for the confusion.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

3 participants