Typescript: Inconsistent behavior for forEach and map when using conditional type

Created on 30 Jan 2020  Â·  11Comments  Â·  Source: microsoft/TypeScript

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:
https://github.com/microsoft/TypeScript/issues/33591


Changing the type of the items field to

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

makes it compile without any errors.

Design Limitation

Most helpful comment

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

All 11 comments

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.

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.

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.

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.

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

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

@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.

@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.

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?

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

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 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)
Was this page helpful?
0 / 5 - 0 ratings

Related issues

dlaberge picture dlaberge  Â·  3Comments

fwanicka picture fwanicka  Â·  3Comments

bgrieder picture bgrieder  Â·  3Comments

jbondc picture jbondc  Â·  3Comments

wmaurer picture wmaurer  Â·  3Comments