Skip to content
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

Type Hints - Inconsistent behavior with types being expanded in type hints on hover #50941

Closed
mitchell-merry opened this issue Sep 25, 2022 · 4 comments
Labels
Not a Defect This behavior is one of several equally-correct options

Comments

@mitchell-merry
Copy link

mitchell-merry commented Sep 25, 2022

(sorry for poor titling - not sure what it should be :) )

Bug Report

🔎 Search Terms

type hints, templates, expanded types / expansion, intersections, conditionals

🕗 Version & Regression Information

This is the behavior in every version I tried (including the nightly build), and I reviewed the FAQ for entries about type hints.

Additionally, I observed it in both TS playground and VSC.

⏯ Playground Link

Playground link with relevant code

(Edited since initial issue to include functions and ^?)

💻 Code

I have types that I want to change depending on a string literal that is passed in via a template argument. The actual details of how this string is parsed is being left out as it is not relevant.

The type hint (what shows when you hover over a variable in an editor / TS playground) has inconsistent behavior surrounding this. The actual type information is correct. I want the types following to show as their un-expanded forms (i.e. Game<""> and Game<"categories">)

What I'm currently doing / what I expect to work: GameA<""> shows expanded, but GameA<"categories"> doesn't.

type GameA<Embed extends string = ""> = {
	id: string;
	name: string;
}
& (Embed extends "categories" ? { categories: string[] } : {});

const a = {} as GameA;               // incorrect - expect type hint to be Game<"">, but shows { id: string; name: string; }!
const b = {} as GameA<"categories">; // correct - type hint shows as Game<"categories">

a.categories; // correct - error (no property)
b.categories; // correct - no error (categories exists and is string[])

A workaround I have found:

type GameB<Embed extends string = ""> = {
	id: string;
	name: string;
} 
& { id: string; }
& (Embed extends "categories" ? { categories: string[] } : {});

const c = {} as GameB;               // correct - type hint shows as Game<"">
const d = {} as GameB<"categories">; // correct - type hint shows as Game<"categories">

c.categories; // correct - error (no property)
d.categories; // correct - no error (categories exists and is string[])

🙁 Actual behavior

Variables of type GameA<""> show up in type hints as { id: string; name: string; } and variables of type GameA<"categories"> show up in type hints as GameA<"categories">. This is inconsistent!

image
image

Intersecting this type with an arbitrary non-empty object causes them to both show up as unexpanded, despite their type not actually changing. In this example the non-empty objects contains a duplicate property, but it does not have to be duplicate to fix it.

image

GameA also is expanded as the return value for functions (when inferred):

image

🙂 Expected behavior

I expected variables of type GameA<""> and GameA<"categories"> to both show up in type hints as just that. (i.e., I expected the behavior of GameB for GameA, and for GameB to be identical to GameA in all aspects)

And for return values of functions, same logic:

image

Note that I want the behavior of un-expanded form here, because I find it much more readable / useful! In my actual project, it looked something like this:

image

(I have fixed this in my project using the GameB workaround).

@RyanCavanaugh RyanCavanaugh added the Not a Defect This behavior is one of several equally-correct options label Sep 26, 2022
@RyanCavanaugh
Copy link
Member

Both of these displays are correct: Game<""> and { id: string; name: string; } are two ways of naming the same type in this program. Quick info does not have a contractual obligation to show one particular form or the other, nor to always show one form or the other (and indeed it's possible to write types that can't be shown in certain forms).

Re consistency, we have heuristics in place to "try to do the right thing". In the case of a simple object type with a spelling in the code itself, showing the type's members is generally preferred, so that happens. In the case of a more complex intersection like Game<"categories">, the alias itself is generally preferred, so that happens.

Switching between various type printbacks in quickinfo is a feature request.

@mitchell-merry
Copy link
Author

That makes sense to me.

While both are correct, one is infinitely more useful to me (i.e. Game<"">) so I do wish I could force it to show like that without the workaround I posted in the original post.

Would that be possible?

@github-actions
Copy link

github-actions bot commented Jun 8, 2023

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

@github-actions github-actions bot closed this as not planned Won't fix, can't repro, duplicate, stale Jun 8, 2023
@brillout
Copy link

brillout commented Oct 6, 2023

Switching between various type printbacks in quickinfo is a feature request.

I created a feature request for it: #56010.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Not a Defect This behavior is one of several equally-correct options
Projects
None yet
Development

No branches or pull requests

3 participants