microsoft / TypeScript

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

No type inferrence of callback arguments inside a tuple union type #55632

Open nenikitov opened 1 year ago

nenikitov commented 1 year ago

🔎 Search Terms

🕗 Version & Regression Information

⏯ Playground Link

https://www.typescriptlang.org/play?#code/C4TwDgpgBAkgdgMwgJwILIOZQLwCgpQA+UA2gIwA0UAFAIYBcUcArgLYBGKAlDgHxQA3APYBLACYBdfEVIAmKtXaMAzsGQi4GHtn7DxEgNy4AxkLiqotTI3hI0mHKUo1a2-gG8oAX0O4gA

💻 Code

type InferArg =
  | [1, (a: number) => void]
  | [2, (b: string) => void];
const arg: InferArg = [1, (a) => { }];

🙁 Actual behavior

I get a compilation error:

Parameter 'a' implicitly has an 'any' type.(7006)

🙂 Expected behavior

a should be inferred to be a number because I already passed 1 as the first tuple element. Type inference should be identical to this code:

type InferArg = [1, (a: number) => void];
const arg: InferArg = [1, (a) => { }];

Additional information about the issue

No response

jcalz commented 1 year ago

I think this is one of the issues that prevents #42987 from working. (discriminated unions of tuples don't seem to get contextual typing the way discriminated unions of non-tuple objects do)

Andarist commented 1 year ago

I had this for a while on my wishlist already - might give it a try soon-ish.

The problem stems from the fact that getApparentTypeOfContextualType doesn't do anything with array literal expressions: https://github.com/microsoft/TypeScript/blob/3f17500cbf0adcdae388a6d161002ab73971f3bf/src/compiler/checker.ts#L29990-L29992

This particular case maybe would just work if we'd call discriminateContextualTypeByObjectMembers (arrays are objects, right? 😉 ) but I think that this requires a special function for the array case. Special consideration has to be made for rest elements (maybe some of the logic could be modeled based on getContextualTypeForElementExpression and changes like this one). On top of that, .length should be ignored completely by it when looking for completions since the array literal might still be "incomplete" - so the completions should only care about what has already been typed in and assume that more things might still be typed in.

nenikitov commented 1 year ago

Thank you for the explanation

fwolff commented 3 weeks ago

I think I have a fix for this issue: https://github.com/fwolff/TypeScript/commit/9eecc3035f4fc82ed994fbb8ce92fa8b9c3cc075. It works with the code snippet given by @nenikitov, the type of the parameter a is correctly inferred to number.

The change is minimal and inspired by what @Andarist suggested in his comment. Before going further, I'd like to get some feedback from @jcalz (I have been discussing tuple issues with him here and on stackoverflow) or anybody having worked on similar topics.

fwolff commented 1 week ago

See my PR: #60434.