Contextual parameters inferred from overloads improvementsΒ #59350
Open
Description
opened on Jul 18, 2024
π Search Terms
contextual parameters functions overloads inference TS7006 argument length
β Viability Checklist
- This wouldn't be a breaking change in existing TypeScript/JavaScript code
- This wouldn't change the runtime behavior of existing JavaScript code
- This could be implemented without emitting different JS based on the types of the expressions
- This isn't a runtime feature (e.g. library functionality, non-ECMAScript syntax with JavaScript output, new syntax sugar for JS, etc.)
- This isn't a request to add a new utility type: https://github.com/microsoft/TypeScript/wiki/No-New-Utility-Types
- This feature would agree with the rest of our Design Goals: https://github.com/Microsoft/TypeScript/wiki/TypeScript-Design-Goals
β Suggestion
Currently, inferring a parameter type from a function with overloads is very finicky. Unused generic parameters, constraints, and parameter length all unnecessarily prevent parameter types from being inferred.
A lot of these limitations are mentioned on #42620, but there's no inherent reason the parameters can't be inferred in many of these cases. This is a suggestion to revisit some of the merge logic to allow a wider variety of cases to be inferred correctly.
π Motivating Example
Here are some examples I've run into recently that I believe are reasonable inference candidates:
Playground: https://tsplay.dev/N5YkVN
export type Fn = {
// t is unused in the first overload
<t>(s: string): void
<t>(t: t): void
}
// first: string | t
export const fn: Fn = (first) => {}
export type Fn2 = {
// remove unused generic param
(s: string): void
<t>(t: t): void
}
// Parameter 'first' implicitly has an 'any' type.
export const fn2: Fn2 = (first) => {}
export type Fn3 = {
<t>(s: string): void
// add constraint to a parameter
<t extends number>(t: t): void
}
// Parameter 'first' implicitly has an 'any' type.
export const fn3: Fn3 = (first) => {}
export type Fn4 = {
(s: string): void
(n: number, b: boolean): void
}
// Second could safely be inferred as boolean | undefined here
// Type '(first: number, second: boolean) => void' is not assignable to type 'Fn4'.
// Target signature provides too few arguments. Expected 2 or more, but got 1.(2322)
export const fn4: Fn4 = (first, second) => {}
π» Use Cases
- What do you want to use this for? Parameter inference
- What shortcomings exist with current approaches? Duplicate definitions and casting
- What workarounds are you using in the meantime? Forcing the signatures to align with hacks like adding unused generic parameters, manually typing them and/or casting.
Activity