Skip to content

TS server returns wrong type if narrowed within the same line #50002

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
m1cm1c opened this issue Jul 22, 2022 Β· 4 comments
Closed

TS server returns wrong type if narrowed within the same line #50002

m1cm1c opened this issue Jul 22, 2022 Β· 4 comments

Comments

@m1cm1c
Copy link

m1cm1c commented Jul 22, 2022

Bug Report

πŸ”Ž Search Terms

type narrowing same line

πŸ•— Version & Regression Information

  • This is the behavior in every version I tried, and I reviewed the FAQ for entries about type narrowing same line

⏯ Playground Link

The playground does not seem to support displaying types, but anyway ...

Playground link with relevant code

πŸ’» Code

async function foo(bar: string | Promise<string>): Promise<void> {
  bar = await bar;
  console.log(bar);
}

πŸ™‚ Expected behavior

When requesting the type at the location of bar in line 2 from TS server, the returned type should be string because await bar can only return string.

πŸ™ Actual behavior

The type is returned as string | Promise<string>:
Screenshot_20220722_163120

In the next line, it works correctly:
Screenshot_20220722_163143

This only happens if the variable on the left pre-exists, i.e. let baz = await bar; or const baz = await bar; does not exhibit this behavior when the type at location of baz is requested.

Additional info

According to Tide's author, the types displayed in the minibuffer are the types returned by TS server: "Tide sends the cursor location to the tsserver and doesn't know much."

@MartinJohns
Copy link
Contributor

But at that point the type of the variable is still string | Promise<string>. Narrowing happens after the assignment.

@m1cm1c
Copy link
Author

m1cm1c commented Jul 23, 2022

But at that point the type of the variable is still string | Promise<string>. Narrowing happens after the assignment.

The variable on the left-hand side of an assignment should have the type that's correct after the assignment because when that variable is used (e.g. bar = (bar = await bar); or console.log(bar = await bar)) it can only take the values that the right-hand side of the assignment can return. Why would you want it to be of a type that includes values that it cannot possibly take? The only sane definition of a type is the set of values that something can take.

Even if for some reason you still disagree, this makes my minimal example of the broken type inference at most 1 line longer.

async function foo(bar: string | Promise<string>): Promise<void> {
  bar = (bar = await bar);
  bar = bar + 'aa';
  console.log(bar);
}

Here, it should be super obvious that the position I requested the type for is after the assignment.
Screenshot_20220723_154958

In fact, in the same line, TS server knows the correct type on the right-hand side of the assignment even though the right-hand side of the assignment lives at an earlier point in time than the left-hand side:

Screenshot_20220723_155401

@MartinJohns
Copy link
Contributor

On a second thought, I think it makes most sense to show the declared type (as it currently does) when assigning. After all, that's the type that determines compatibility.

@m1cm1c
Copy link
Author

m1cm1c commented Jul 23, 2022

On a second thought, I think it makes most sense to show the declared type (as it currently does) when assigning. After all, that's the type that determines compatibility.

Okay, this is a fair point. TS server actually seems to behave consistently when setting this as the goal. At least I wasn't able to find any inconsistencies with this. Closing the issue. Thank you for this comment.

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

2 participants