-
Notifications
You must be signed in to change notification settings - Fork 13k
Description
🔎 Search Terms
method overload, generic mapped types, conditional generics, overload resolution, optional parameters, Parameters utility type, ReturnType, ConfigurableAPI, overload lost mapped type, call method generic, generic constraint overload
🕗 Version & Regression Information
- This is the behavior in every version I tried, and I reviewed the FAQ for entries about overloads and generics
- I was unable to test this on prior versions because the behavior has been consistent since at least 4.9.5
⏯ Playground Link
💻 Code
// Minimal reproduction showing the bug
class API<T extends Record<string, (...args: any[]) => any>> {
constructor(private methods: T) {}
// Method overloads for better DX
call<K extends keyof T>(method: K): ReturnType<T[K]>;
call<K extends keyof T>(method: K, ...args: Parameters<T[K]>): ReturnType<T[K]>;
call<K extends keyof T>(method: K, ...args: any[]): ReturnType<T[K]> {
return this.methods[method](...args);
}
}
// Test with various method signatures
const api = new API({
// No parameters
getString: () => "hello",
// Required parameter
getNumber: (multiplier: number) => 42 * multiplier,
// Optional parameter
processData: (data: string, options?: { uppercase?: boolean }) =>
options?.uppercase ? data.toUpperCase() : data,
// Multiple parameters with optional
complexMethod: (a: number, b: string, c?: boolean) =>
c ? `${b}-${a}` : `${a}-${b}`
});
// ❌ BUG: Overload resolution fails
// Case 1: No parameters - should match first overload
const str = api.call('getString');
// Error: Expected 2 arguments, but got 1.
// Case 2: Optional parameter - fails
const data1 = api.call('processData', 'test');
// Error: Argument of type 'string' is not assignable to parameter of type 'never'.
// Case 3: Multiple parameters with optional
const result1 = api.call('complexMethod', 1, 'test');
// Error: Argument of type 'number' is not assignable to parameter of type 'never'.
🙁 Actual behavior
TypeScript fails to correctly resolve method overloads when using generic mapped types with Parameters<T[K]> utility type:
-
Methods with NO parameters: TypeScript expects 2 arguments instead of matching the first overload
call<K>(method: K)
- Error: "Expected 2 arguments, but got 1"
-
Methods with OPTIONAL parameters: TypeScript cannot determine that optional parameters can be omitted
- Error: "Argument of type 'string' is not assignable to parameter of type 'never'"
-
The overload resolution always picks the second overload even when it shouldn't
The generic constraint with Parameters<T[K]>
appears to confuse the overload resolution mechanism.
🙂 Expected behavior
The overload resolution should work correctly based on the number and types of arguments:
-
When calling
api.call('getString')
with no additional arguments, TypeScript should match the FIRST overload and return typestring
-
When calling
api.call('processData', 'test')
with optional parameters omitted, it should be valid and infer the correct types -
Optional parameters should be truly optional in the method call
The compiler should be able to:
- Distinguish between the two overloads based on argument count
- Properly handle optional parameters in generic contexts
- Correctly infer return types based on the matched overload
This pattern is essential for type-safe API wrappers, RPC clients, and plugin systems.
Additional information about the issue
This issue significantly impacts developer experience when building type-safe wrappers around dynamic method collections.
Real-world use cases affected:
- GraphQL/REST client generators
- RPC frameworks (tRPC, JSON-RPC)
- Database ORMs with dynamic query methods
- Plugin architectures with registered methods
- Testing utilities for creating typed mocks
Current workarounds (all suboptimal):
- Use type assertions (
as any
) - loses type safety - Avoid overloads entirely - poor API ergonomics
- Create individual wrapper methods - defeats the purpose of generic approach
Related issues:
- Instantiation expressions #47607 - Generic inference with Parameters utility type
- Function argument inference only handles one overload #26591 - Overload resolution with generic constraints
- LEGO: Pull request from lego/hb_998_20171216051004015 to master #20732 - Overload gets lost in mapped type with conditional type
Without proper overload resolution, library authors must sacrifice either type safety or developer experience.