Skip to content

Incorrect overload selected for Array.prototype.map(...) #47571

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

Closed
oleg-slapdash opened this issue Jan 24, 2022 · 6 comments
Closed

Incorrect overload selected for Array.prototype.map(...) #47571

oleg-slapdash opened this issue Jan 24, 2022 · 6 comments
Labels
Design Limitation Constraints of the existing architecture prevent this from being fixed

Comments

@oleg-slapdash
Copy link

oleg-slapdash commented Jan 24, 2022

Bug Report

πŸ”Ž Search Terms

overload, array.prototype.map

πŸ•— Version & Regression Information

  • This is the behavior in every version I tried.

⏯ Playground Link

Playground link with relevant code

πŸ’» Code

declare type Obj = {a: true};
declare const arr: Array<string | Obj>;
declare function func(url: string | Obj): string;
declare function func<T extends Obj | string>(url: T): string | null;
const fails: string[] = arr.map(func);
// ^^^ Type '(string | null)[]' is not assignable to type 'string[]'.
const works: string[] = arr.map((v) => func(v));



interface CustomArray<T> {
    map<U>(callbackfn: (value: T) => U): U[];
}
declare const arr2: CustomArray<string | Obj>;
const fails2: string[] = arr2.map(func);
// ^^^ Type '(string | null)[]' is not assignable to type 'string[]'.
const works2: string[] = arr2.map((v) => func(v));

πŸ™ Actual behavior

It fails to select the correct overloard.

πŸ™‚ Expected behavior

It is supposed to select the correct overload.

@RyanCavanaugh
Copy link
Member

Inference doesn't do normal overload resolution per se, instead it performs structural inference between the signatures to try to line things up.

Is there a real definition of func ? This problem can almost always be avoided in non-pathological cases.

@RyanCavanaugh RyanCavanaugh added the Design Limitation Constraints of the existing architecture prevent this from being fixed label Jan 25, 2022
@oleg-slapdash
Copy link
Author

@RyanCavanaugh, this is an approximation. Basically, this is a helper function that gets a string property of an object or returns a string if a string is passed instead of an object.

type ObjWithFoo = {foo: string};

declare function getFoo(foo: string | ObjWithFoo): string;
declare function getFoo(url: ObjWithFoo | string | null | undefined): string | null | undefined;

declare const arr: Array<string | ObjWithFoo>;

export const works: string[] = arr.map((v) => getFoo(v));
export const fails: string[] = arr.map(getFoo);
// Err: ^^^ Type '(string | null | undefined)[]' is not assignable to type 'string[]'

@RyanCavanaugh
Copy link
Member

Conditional types will get instantiated, so I'd recommend this:

type ObjWithFoo = {foo: string};

declare function getFoo<T extends ObjWithFoo | string | null | undefined>(url: T): T extends undefined | null ? T : string;

declare const arr: Array<string | ObjWithFoo>;

export const works: string[] = arr.map((v) => getFoo(v));
export const alsoWorks: string[] = arr.map(getFoo);

declare const arrU: Array<string | ObjWithFoo | undefined>;
export const chk = arrU.map(getFoo); // Array<string | undefined>

@oleg-slapdash
Copy link
Author

@RyanCavanaugh thanks!

@maple5233
Copy link

Conditional types will get instantiated, so I'd recommend this:

type ObjWithFoo = {foo: string};

declare function getFoo<T extends ObjWithFoo | string | null | undefined>(url: T): T extends undefined | null ? T : string;

declare const arr: Array<string | ObjWithFoo>;

export const works: string[] = arr.map((v) => getFoo(v));
export const alsoWorks: string[] = arr.map(getFoo);

declare const arrU: Array<string | ObjWithFoo | undefined>;
export const chk = arrU.map(getFoo); // Array<string | undefined>

Will this bug be fixed? The way using the conditional types looks like a work-around.

@ExplodingCabbage
Copy link

ExplodingCabbage commented Mar 14, 2025

Summary of others' points from duplicate #61398, for the benefit of anyone else landing here from search:

  • This was never fixed in any way and the "completed" closure reason here should be ignored (as with on many other closed issues)
  • The "Design Limitation" label indicates the actual reason for closure
  • Per https://github.com/Microsoft/TypeScript/wiki/FAQ#this-is-closed-but-should-be-open-or-vice-versa, that label is basically just a "we don't see how to fix this right now" label, not necessarily a "there is a profound design reason this is unfixable" label. PRs to fix it remain welcome despite the closure.
  • It's specifically the case that whenever an overloaded function is passed as an argument to another function, its last overload signature is treated as its signature and all preceding signatures are ignored. There is something of a logic to this in that the last signature will sometimes be a "catch-all" signature and thus most likely to not cause a type error (though for an overloaded function without a catch-all signature, this choice ends up being effectively completely arbitrary).
  • There was a promising-looking attempt to fix this at Improve overload and generic signature inference: Inference alternatives and linked inferencesΒ #52944, but apparently it had performance issues, and it's been sitting untouched for a couple of years now. Possibly there are profound reasons those performance issues are hard to solve though that's hard to tell for a casually-observing outsider.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Design Limitation Constraints of the existing architecture prevent this from being fixed
Projects
None yet
Development

No branches or pull requests

4 participants