Skip to content
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

Inconsistent behavior for forEach and map when using conditional type #36521

Closed
sassanh opened this issue Jan 30, 2020 · 11 comments
Closed

Inconsistent behavior for forEach and map when using conditional type #36521

sassanh opened this issue Jan 30, 2020 · 11 comments
Labels
Design Limitation Constraints of the existing architecture prevent this from being fixed

Comments

@sassanh
Copy link

sassanh commented Jan 30, 2020

TypeScript Version: 3.8.0-dev.20200128

Search Terms:

Code

interface Section<T extends number | string> {
  items: T extends number ? number[] : string[];
}

export function test<T extends number | string>(arg: Section<T>): void {
  arg.items.forEach((): void => console.log());
  arg.items.map((): void => console.log());
}

Expected behavior:

Typescript to either can determine the signature of both the map callback AND forEach callback or to understand none.

Actual behavior:

It doesn't have any problem with forEach but raises this error for the map clause:

This expression is not callable.
  Each member of the union type '(<U>(callbackfn: (value: number, index: number, array: number[]) => U, thisArg?: any) => U[]) | (<U>(callbackfn: (value: string, index: number, array: string[]) => U, thisArg?: any) => U[])' has signatures, but none of those signatures are compatible with each other.

Playground Link:
http://www.typescriptlang.org/play/?ts=3.8.0-dev.20200128&ssl=9&ssc=1&pln=1&pc=1#code/JYOwLgpgTgZghgYwgAgMoQWYB7EAeAFWQgA9IQATAZ2RAFcBbAI2mQB9kqwpQBzAPmQBvAFDJkwSAyoAuZEVLlqtRiyjIA-CubQA2gF1kcrjxC8DAbhEBfESNIAHbFDDIYdEJhwhkkLoWIyCEoaeh11DhM+fgAKOCheOXQvXEJ+AEo5ADdsYAphMWR43gA6SQhpEphnAFFEAAsYmMzkHLzkAF5BBFwqbAAbCBL+7F5m9KtxYrKpKhKGOAcmlrb8ruQekD7B4dHxq1sgA

Related Issues:
#33591


Changing the type of the items field to

  items: (T extends number ? number : string)[];

makes it compile without any errors.

@jcalz
Copy link
Contributor

jcalz commented Jan 30, 2020

Looks like another duplicate of #7294, mentioned in documentation as caveats for improved behavior for calling union types:

This new behavior only kicks in when at most one type in the union has multiple overloads, and at most one type in the union has a generic signature. That means methods on number[] | string[] like map (which is generic) still won’t be callable.

On the other hand, methods like forEach will now be callable, but under noImplicitAny there may be some issues.

@sassanh
Copy link
Author

sassanh commented Jan 30, 2020

I'm not sure if it's the same thing, I just edited my issue I think it was misleading, the issue I'm reporting is that typescript should have same behavior for forEach and map, it's raising an error for map but none for forEach.

@jcalz
Copy link
Contributor

jcalz commented Jan 30, 2020

Please read the TS3.3 release notes for Improved behavior for calling union types, including the full Caveats section, and let me know if there's something in your issue that isn't covered by that.

@sassanh
Copy link
Author

sassanh commented Jan 31, 2020

It's explicitly said in the caveats that map doesn't work and forEach works, apparently because map is a generic because of its return type. So I guess the developers are already aware of this issue and we can close this.

@sassanh sassanh closed this as completed Jan 31, 2020
@sassanh
Copy link
Author

sassanh commented Jan 31, 2020

I just noticed that issue is closed without resolving the issue described here, so I guess it should stay open.

@sassanh sassanh reopened this Jan 31, 2020
@RyanCavanaugh RyanCavanaugh added the Design Limitation Constraints of the existing architecture prevent this from being fixed label Feb 6, 2020
@RyanCavanaugh
Copy link
Member

map is generic (because it has an output type) and forEach isn't.

@sassanh
Copy link
Author

sassanh commented Feb 6, 2020

@RyanCavanaugh map being a generic doesn't imply that it should raise this error, I understand that it matters internally, but it's still an issue with typescript and I guess you want it to be resolved and in that case I guess we should keep related issues open, closing all issues related to this problem doesn't help with solving it, instead we can consider changing the title unless you know another open issue is tracking this problem.

@RyanCavanaugh
Copy link
Member

RyanCavanaugh commented Feb 6, 2020

@sassanh Please don't tell us our what our own metadata means. For us, "Open" means that we have the ability to schedule engineering work to address the problem; "Closed" means that it doesn't. This issue being closed doesn't mean that we're unaware of the limitation.

@sassanh
Copy link
Author

sassanh commented Feb 6, 2020

I wasn't telling you what your metadata mean, I'm sorry if it was interpreted that way, I among many others usually use issue trackers to reflect "awareness" about problems on it and we usually close an issue only when it's resolved, seems like it's different here, is there any document explaining how issues are tracked in TypeScript repository that I can read so that I can avoid such misunderstandings and stay productive in my future bug reports?

@RyanCavanaugh
Copy link
Member

Fair enough; I owe you a FAQ update on that one

@Luxcium
Copy link

Luxcium commented Feb 29, 2020

Is there any workaround for the issue?

@sassanh Please don't tell us our what our own metadata means. For us, "Open" means that we have the ability to schedule engineering work to address the problem; "Closed" means that it doesn't. This issue being closed doesn't mean that we're unaware of the limitation.

Ho @sassanh I dream of a world where one doesn't fear to comment on <GITHUB | STACK OVERFLOW | REDIT | any> it may seem rude to say «Please don't tell us[...] what our own metadata means.» it did to me but I don't want to be unconstructive...

the rest of the comment is interesting and important to me because I didn't know that «For [you], "Open" means that [you] have the ability to schedule engineering work to address the problem; "Closed" means that it doesn't. This issue being closed doesn't mean that [you're] unaware of the limitation.»

Maybe my code is just wrong and I don't know

My code:

  public listMap<R>(
fn: (
      value: T | Promise<T>,
      index: number,
      array: T[] | Promise<T>[]
    ) => R | Promise<R>,
    thisArg?: any
  ) {
    if (this.value !== null) {
       return new MaybeList<R>(this.value.map<R>(fn, thisArg))
    }
    return new MaybeList<null>();
  }

this.value is defined by private value!: T[] | Promise<T>[] | null;

error message: (this.value.map())

  This expression is not callable. Each member of the union type 
  '(<U>(callbackfn: (value: T, index: number, array: T[]) => 
  U, thisArg?: any) => U[]) | 
  (<U>(callbackfn: (value: Promise<T>, index: number, array: Promise<T>[]) => 
  U, thisArg?: any) => U[])' 
  has signatures, but none of those signatures are compatible with each other.
  ts(2349)

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