-
Notifications
You must be signed in to change notification settings - Fork 12.6k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Overload gets lost in mapped type with conditional type #29732
Comments
From #21496. I suspect this is probably a design limitation. |
But that was before we had tuple types. This overload: interface Overloads {
foo(a: string): void;
foo(a: number, b: string): void;
} is equivalent to interface NoOverloads {
foo(...args: [string] | [number, string]): void;
} which works correctly. I would use that workaround, but I don't have control over libraries that use overloads. I would at least expect the compiler to treat the above interface equivalently to the below. If the return type is different, it's also possible with conditional types: interface Overloads {
foo(a: string): string;
foo(a: number, b: string): number;
} interface NoOverloads {
foo<P>(...params: P): P extends [number, string] ? number : string;
} So I don't see a reason why overloads would still be impossible for the compiler to handle |
The tuple approach seems plausible; perhaps that could be a proposal included in the issue? The conditional type approach elicits further problems because generic parameters do not get unified. |
I would just like to point out that interface NoOverloads {
foo(...args: [string] | [number, string]): void;
}
const Bar: NoOverloads = {
foo: function (a, b) {
return;
}
} actually does not work currently. I'm personally very interested in seeing typeable overloads. I'm struggling to get TypeScript to accept my current typings for one of my projects: here it is. |
Not sure if it's the same issue but I came here to report this behavior: type Post = { body: string }
type Member = { name: string }
type methods = {
on(event: 'new_post', payload: Post): void
on(event: 'member_add', payload: Member): void
}
type Params = Parameters<methods['on']>
// Expected Params type: ["new_post", Post] | ["member_add", Member]
// Current behavior: ["member_add", Member] (only gets the last one) Related use case: probot/probot#858 |
Any news on this? Just hit this bug myself. |
This limitation has been a thorn in my side for multiple projects. Can anything be done to assist? |
@ClickerMonkey For non generic overloaded functions you can use the approach described here to improve the situation |
I stumbled upon this issue while trying to generate generic overloads programmatically. Here's a workaround: Unions are not treated like overloads, but intersections are. So, we just need to find a way to create an intersection of interfaces containing the overloads. We can do that by wrapping the function signatures in interfaces. |
Thank you @graup for the workaround and the example I made your Playground simpler so it's easier to follow the idea: |
Hi @felixfbecker! // still work in progress, I'm trying to further simplify it.
export type SetState<T> = undefined extends T ? {
(value: T | ((draft: T) => T | void), meta?: { logger?: Logger }): T,
(value: undefined | typeof nothing | ((draft: T) => typeof nothing), meta?: { logger?: Logger }): undefined,
(value: (draft: T) => Promise<T | void>, meta?: { logger?: Logger }): Promise<T>,
(value: (draft: T) => Promise<typeof nothing>, meta?: { logger?: Logger }): Promise<undefined>
} : {
(value: T | ((draft: T) => T | void), meta?: { logger?: Logger }): T,
(value: (draft: T) => Promise<T | void>, meta?: { logger?: Logger }): Promise<T>,
} It is very difficult to convert that to use only inference. 🍻 |
Since we can't use tuple in rest param type, I don't think we can use conditional type to replace function overloads. e.g. type F = {
() => number,
(value: string) => string
}
// vs
type F = <P extends Array<string>>(...args: P): P extends [] ? number : (P extends [string] ? string : never)
// this won't work
<P extends [] | [string]>(...args: P): ...
let f: F
f('a', 'b') // function overload prevents this, but conditional type can't also, user can't easily see what params As compare to function overload where you can name the arguments properly. |
Arrived here from the same core issue, but with a different use case. // reduced script
type SplitParameter = Parameters<string['split']>[0];
function split(source: string, by: SplitParameter): string[] {
return source.split(by);
} This only found the ES2015Splitter variant of Checking the rest of the comments, I ended up doing a union, but it still failed the overloads: type ES5Delimiter = string | RegExp;
type ES2015Splitter = {
[Symbol.split](string: string, limit?: number): string[];
};
function split(source: string, by: ES5Delimiter|ES2015Splitter): string[] {
// @ts-expect-error this fails both overloads :(
return source.split(by);
}
// completely superfluous guards
function safeSplit(source: string, by: ES5Delimiter|ES2015Splitter): string[] {
if (by instanceof RegExp || typeof by === 'string') {
return source.split(by);
}
return source.split(by);
} |
TypeScript Version: 3.4.0-dev.201xxxxx
Search Terms: mapped type overload
Code
Expected behavior:
No error, overload should be maintained.
This makes it impossible to use this pattern with popular types that contain overloads, like Rx
Observable
pipe()
/subscribe()
.The
ProxiedObject
type is used in https://github.com/GoogleChromeLabs/comlink.Actual behavior:
Overload gets lost, compile error when trying to call the first overload.
Playground Link: link
The text was updated successfully, but these errors were encountered: