microsoft / TypeScript

TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
https://www.typescriptlang.org
Apache License 2.0
99k stars 12.29k forks source link

Signature help stopped expanding type alias in TypeScript 5.5 #59078

Open DRKolev-code opened 2 days ago

DRKolev-code commented 2 days ago

🔎 Search Terms

type inference

🕗 Version & Regression Information

⏯ Playground Link

TS Playground

💻 Code

const test = {
  t1: {
    t11: {
      v1: "${in1} = ${in2}"
    }
  }
} as const;

type Values<T> = T[keyof T];
type ExtractInputs<T> =
  T extends `${string}\$\{${infer K}\}${infer R}` ? K | ExtractInputs<R> :
  T extends object ? ExtractInputs<Values<T>> :
  never;

type Inputs = ExtractInputs<typeof test.t1>;

function my_func(inputs: Record<Inputs, string>) {}

🙁 Actual behavior

my_func( does not show that the function accepts {in1, in2}

🙂 Expected behavior

my_func( // In TypeScript 5.4.5, this provides correct suggests { in1, in2 }

Additional information about the issue

No response

Andarist commented 2 days ago

I'm not sure what this issue is about. I think it might be about the signature help change because it has changed as follows:

-my_func(inputs: Record<"in1" | "in2", string>): void
+my_func(inputs: Record<Inputs, string>): void

This is just a display change (bisected to this diff). Inputs is equivalent to "in1" | "in2" after all. If you hover over Input using 5.5 you can confirm this.

FWIW, object property completions work just fine in both versions.

DRKolev-code commented 2 days ago

When you say it is just a display change do you mean this report is not precise or regarding its importance?

Either way the outcome is a regression. Displaying (inputs: Record<Inputs, string>): is not helpful. The inputs can be arbitrary and the test object with the code to construct the function does not have to be in the same place where the function is going to be used.

fatcerberus commented 2 days ago

There are no hard-and-fast rules for how type aliases are expanded or not in type display. It's sort of a black art as types can get arbitrarily complex (the type system is Turing complete) but generally TS tries to preserve the type aliases in display since they are often more informational than their full expansion. i.e. if you have a generic type = Foo<T> = [some huge object type using T] you usually want to see Foo<T> displayed and not whatever it ultimately resolves to.

If you hover over Input using 5.5 you can confirm this.

I always find myself wanting the ability to hover/click on type aliases in signature help and/or other hover tips to drill down on them. It'd be awesome.

DRKolev-code commented 2 days ago

I am not sure how to phrase this. Changing the behaviour of the displayed hints being tied to a language update is strange given it is not caused by an underlying language regression.

If you don't have Input in a convenient place so that you can hover over it then the Input type is pointless for the end user. It carries no information as to what you actually need to pass to the function. In this case it would be much more useful to be able to construct the individual in1 and in2 as separate arguments for my_func but as far as I know typescript does not support this. There could be n-amount of separate inputs and it is controlled by the presence of ${} in the strings.

Given the PR mentioned by @Andarist (which I how no idea what it does or why it affects so many files) and @fatcerberus explanation I can say that a need for controlling the display depth by us is needed. This still remains a regression but intentional?

Andarist commented 2 days ago

Given the PR mentioned by @Andarist (which I how no idea what it does or why it affects so many files)

It's a diff between 2 nightly versions - it refers to a couple of landed PRs, not a single one. If I'd have to make a guess this is caused by https://github.com/microsoft/TypeScript/pull/58085

This still remains a regression but intentional?

Type display was never treated as a binding contract so I don't think anybody from the TS team would call this a regression.

If you don't have Input in a convenient place so that you can hover over it then the Input type is pointless for the end user.

I don't think this is true. It sometimes is more helpful but sometimes isn't. It's contextual and the language server has freedom over the heuristics it uses to determine if any given type should be "expanded" or not.

Those 2 being displayed differently is weird and inconsistent to me though: TS playground

DRKolev-code commented 2 days ago

Those 2 being displayed differently is weird and inconsistent to me though

If you point it to Record and Inputs you get different results. It seems it is a way to indicate what to resolve: TS Playground

Can this be used somehow to set what my_func( should display?

andrewbranch commented 1 day ago

I think the underlying desire here is pretty well covered by #59029.