Skip to content

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

@mitchell-merry

Description

@mitchell-merry

(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).

Metadata

Metadata

Assignees

No one assigned

    Labels

    Not a DefectThis behavior is one of several equally-correct options

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions