Skip to content

Misleading type hint when narrowed at declaration time #46731

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
denis-sokolov opened this issue Nov 8, 2021 · 9 comments
Closed

Misleading type hint when narrowed at declaration time #46731

denis-sokolov opened this issue Nov 8, 2021 · 9 comments
Labels
Working as Intended The behavior described is the intended behavior; this is not a bug

Comments

@denis-sokolov
Copy link

denis-sokolov commented Nov 8, 2021

Bug Report

🔎 Search Terms

variable declaration, type hint

🕗 Version & Regression Information

This is the behavior in every version I tried, and I reviewed the FAQ for entries about Common "Bugs" That Aren't Bugs and Type System Behavior

Earliest version tested: 3.3.3333
Last version tested: 4.6.0-dev.20211105

⏯ Playground Link

Playground link with relevant code

💻 Code

type Foo = { left: string } | { right: string }

const a: Foo = { left: '' }
console.log(a.left);

🙁 Actual behavior

Type hint for const a on the line of declaration is Foo.
Type hint for const a on the line of use is { left: string }.

🙂 Expected behavior

Type hint for const a on the line of declaration is { left: string }, consistent with the type hint on the next line.

Showing the type of a as Foo is at least a visual bug. If a is Foo, how come I can access a.left on the next line?

As a more opinionated, stronger statement, I would not expect type narrowing here at all. I have very clearly annotated that a is Foo, I don't want any other type to be inferred. I have annotated it as Foo because probably in the future the value of a will be more dynamic and I want to make sure the code below supports that.

@denis-sokolov denis-sokolov changed the title Misleading Misleading type hint when narrowed at declaration time Nov 8, 2021
@MartinJohns
Copy link
Contributor

As a more opinionated, stronger statement, I would not expect type narrowing here at all.

The opposite would be much worse. Just take this example:

const val: string | number = 'abc';
val.toLocaleLowerCase();

Would you expect an error here? Surely not, because you always assign a string. This is the reason why the type narrows on assignment.

@denis-sokolov
Copy link
Author

I would expect an error, yes! If we knew val is always a string, then it should be a string. Yet the explicit, intentional annotation made by the author clearly indicates that val might be a number, if not now, then in the future. I don’t expect others to have the same intuition as me, but I assure you, I do hold mine strongly and consistently.

@jcalz
Copy link
Contributor

jcalz commented Nov 8, 2021

The narrowing part is a feature, not a bug. See the TS documentation: https://www.typescriptlang.org/docs/handbook/2/narrowing.html#assignments

The quick info type display is also as intended but I don’t know of an official doc about it… I’ve seen someone on the TS team mention it before in an issue though. Maybe I can find it.

@denis-sokolov
Copy link
Author

@jcalz, that documentation does not explain the case where there is an explicit type annotation on the variable. Why does type inferrence overwrite the type annotation? But regardless, I understand this is a matter of opinion and design. The quick info type display has to be a bug, though. It misrepresents the type of the variable and is inconsistent with the same type hint on the same variable on the next line.

@jcalz
Copy link
Contributor

jcalz commented Nov 8, 2021

There's #45870, which is asking for some way to see both the narrowed type and the original type of a narrowed expression. Right now quickinfo shows you a single type for any given expression, so it's going to be "misleading" no matter what; some people (e.g., you) expect to see the annotated type and others expect to see the narrowed type.

@andrewbranch andrewbranch added the Working as Intended The behavior described is the intended behavior; this is not a bug label Nov 8, 2021
@denis-sokolov
Copy link
Author

I believe the variable only has one type. It’s the set of values that that variable can hold. The difference between the annotated and the narrowed types is an implementation detail.

Ignoring my judgement for a moment, what are the practical benefits of showing the annotated type on declaration? The annotated type is right there already.

@fatcerberus
Copy link

Technically a variable does have two types at any given time, and this is not just an implementation detail: There's the type it's been narrowed to based on control flow analysis, which affects where it can be assigned, and the declared type, which affects what you can assign to it. Both are observable.

@denis-sokolov
Copy link
Author

@fatcerberus, fair enough, but that only applies to mutable bindings. For immutable bindings, which are the default, in my opinion, there is only the former.

@typescript-bot
Copy link
Collaborator

This issue has been marked 'Working as Intended' and has seen no recent activity. It has been automatically closed for house-keeping purposes.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Working as Intended The behavior described is the intended behavior; this is not a bug
Projects
None yet
Development

No branches or pull requests

6 participants