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

Wrong return type inference in a generic function using Promise.all #37664

Closed
Luxcium opened this issue Mar 28, 2020 · 12 comments
Closed

Wrong return type inference in a generic function using Promise.all #37664

Luxcium opened this issue Mar 28, 2020 · 12 comments
Labels
Needs More Info The issue still hasn't been fully clarified

Comments

@Luxcium
Copy link

Luxcium commented Mar 28, 2020

Can I resolve this problem below without the awaited keyword? how can I infer return type without having to use as unknown as R[] and providing a hardcoded type each time?

    // Promise.all(this.fork) take a T1[] where T1 is a Promise<number>
    // and return a Promise<T2[]> where T2 is a number
    // therefore T1 and T2 are not both «T»

TypeScript Version: 3.9.0-dev.20200326

Search Terms:
Promise.all
promise.all generic
generic awaited
awaied

Code

class MyMaybeList<T = any>, le 2020-03-24 à 03 45 35 EST

Code as text below -- Click to enlarge image -- #Pop N' Lock Theme by Luxcium ✨

export class MyMaybeList<T = any> {
  private constructor(values: T[]) {
    this.values = values;
  }

  private values: T[];

  // get =======================================================-| fork() |-====

  public get fork(): T[] {
    return this.values != null ? this.values.slice() : [].slice();
  }

  // public =====================================================-| map() |-====

  public map<R = any>(
    fn: (val: T, index: number, array: T[]) => R
  ): MyMaybeList<R> {
    return MyMaybeList.of(...this.values.map(fn));
  }

  // static ======================================================-| of() |-====

  public static of<TVal>(...val: TVal[]): MyMaybeList<TVal> {
    if (val == null || !val.length) return new MyMaybeList<TVal>([]);
    return new MyMaybeList<TVal>([...val]);
  }

  // async =====================================================-| will() |-====

  public async will /* <R> */() /* : Promise<MyMaybeList<R>> */ {
    // Promise.all(this.fork) take a T1[] where T1 is Promise<number>
    // and return a Promise<T2[]> where T2 is a number
    // therefore T1 and T2 are not both «T»
    console.log(this.fork);

    const willThen = Promise.all(this.fork);

    const thenWill = await willThen;
    return MyMaybeList.of(...thenWill);
  }
}

Example, le 2020-03-24 à 03 46 09 EST

Code as text below -- Click to enlarge image -- #Pop N' Lock Theme by Luxcium ✨

const oneToTen = MyMaybeList.of(1, 2, 3, 4, 5, 6, 7, 8, 9, 10);
const powersOneToTen = oneToTen.map(async val => val * val);

// "log->" powersOneToTen: MyMaybeList<Promise<number>>
console.log(powersOneToTen);

// "log->" awaitedList: MyMaybeList<Promise<number>>
// instead of a -> MyMaybeList { Promise<number> }
// in fact is a -> Promise {  MyMaybeList<number> }
const awaitedPowersOneToTen = powersOneToTen.will /* <unnknow> */();
awaitedPowersOneToTen.then(awaitedList => console.log(awaitedList));

// Promise { <pending> } will resolve into ->
console.log(awaitedPowersOneToTen);

Console logs le 2020-03-24 à 03 46 58 EST

Click to enlarge image -- #Pop N' Lock Theme by Luxcium ✨

Now that #37610 has reverted the awaited type I don't know what to do I have provided an example above but my real-life situation is as follow :

I was relying on this solution because I have a Promise<MaybeList<Promise<ISymbolSearchResult[]>>>
where MaybeList is an array abstraction so I am using promise.all to remove the inner Promise I don't know what to do now to avoid using «as unknown as MaybeList<ISymbolSearchResult[]>» inside of my async function (which should be returning the Promise<MaybeList<ISymbolSearchResult[]>> instead of the Promise<MaybeList<Promise<ISymbolSearchResult[]>>>) *Note that I have an array of ISymbolSearchResult inside an arrayLike of type MaybeList so, in this case, an array inside of an array

Actual behavior:
Promise<MyMaybeList<Promise<number>>> (as in 3.9.0-dev.20200326)

Expected behavior:
Promise<MyMaybeList<number>> (as in 3.9.0-dev.20200324)

Related Issues:
#37610, #37526, #33055, #30551, #35998, #37534, #37115, #34925
maybe also:
#9998, #33707, #36232, #35136, #33562, #34883, #31394
as discussed in TypeScript 3.9 Iteration Plan #37198

@Luxcium Luxcium changed the title Can not get correct return type inference when using Promise.all in a generic function Wrong return type inference in a generic function when using Promise.all Mar 28, 2020
@Luxcium Luxcium changed the title Wrong return type inference in a generic function when using Promise.all Wrong return type inference in a generic function using Promise.all Mar 28, 2020
@adrienboulle
Copy link

adrienboulle commented Apr 8, 2020

If it can help i have this example

Here the thing that break type inferring is the fact that one of the promises has unknown type even if its return value is not used

@Luxcium
Copy link
Author

Luxcium commented Apr 9, 2020

If it can help i have this example

Here the thing that breaks type inferring is the fact that one of the promises has unknown type even if its return value is not used

**I am sorry @adrienboulle I am not sure what I should be looking for in your example you mention I think the line 13 where getNumbersB() is not used but I don't see the problem ... **

Capture d’écran, le 2020-04-08 à 22 52 45

click to enlarge image -- #Pop N' Lock Theme by Luxcium ✨

At this moment I am still using 3.9.0-dev.20200324 because for some random reason I had that problem on 03-23 got that fix by chance on the 24th and it got reverted the day after ...

@Luxcium
Copy link
Author

Luxcium commented Apr 9, 2020

ok (OOPS!) I forgot to install the typescript@latest (3.8.3) and I think the typescript@next is not breaking (3.9.0-dev.20200408) so I can upgrade from 3.9.0-dev.20200324

Capture d’écran, le 2020-04-08 à 23 01 50

click to enlarge image -- #Pop N' Lock Theme by Luxcium ✨

yes I have « 🍒» inside of my error messages LoL 😹

@adrienboulle
Copy link

haha i can't blame you tu add some joy on your code!
So if i understand correctly, 3.9.0 fixes the issue ?

@RyanCavanaugh RyanCavanaugh added the Needs More Info The issue still hasn't been fully clarified label Apr 20, 2020
@RyanCavanaugh
Copy link
Member

I'm not really sure what's going on here; we need a concise problem description to work from

@Luxcium

This comment has been minimized.

@Luxcium
Copy link
Author

Luxcium commented Apr 21, 2020

The TypeScript playground site was not working correctly for me when I have first opened this issue it is now working ...

With: 3.9.0-dev.20200324 the following code awaitedPowersOneToTen.then(awaitedList => console.log(awaitedList)); when I mouse over awaitedList it is a MyMaybeList<number>

But with: 3.9.0-dev.20200420 the following code awaitedPowersOneToTen.then(awaitedList => console.log(awaitedList)); when I mouse over awaitedList it is a MyMaybeList<Promise<number>>

@Luxcium
Copy link
Author

Luxcium commented Apr 21, 2020

I am using Promise.all on something like an array or a list (MyMaybeList) and I want to infer the type without explicitly passing it (in a generic) ... Promise.all should remove the Promise inside of my list so I can get MyMaybeList<number> a list of "numbers" instead of gettingMyMaybeList<Promise<number>> a list of "Promises of a number" which is the type that I have before using Promise.all

@Luxcium
Copy link
Author

Luxcium commented May 4, 2020

I got this error message and don't know what I should do ...
error(TS2345): Argument of type 'awaited T' is not assignable to parameter of type 'T'.
'T' could be instantiated with an arbitrary type which could be unrelated to 'awaited T'.

Capture d’écran, le 2020-05-04 à 17 38 39

Code as text below -- Click to enlarge image -- #Pop N' Lock Theme by Luxcium ✨

TypeScript code: lib/functional/promise-or-not.ts

// ... [more code]

export async function thenified<T>(
  promise: Promise<T>,
  funct: <R>(t: T) => R,
): Promise<any> {
  return promise.then(
    t => funct(t)
  );
}

Output: tsc Version 3.9.0-dev.20200324

% ❯  tsc # 3.9.0-dev.20200324 
lib/functional/promise-or-not.ts:10:16 - error TS2345: Argument of type 'awaited T' is not assignable to parameter of type 'T'.
  'T' could be instantiated with an arbitrary type which could be unrelated to 'awaited T'.

10     t => funct(t)
                  ~
Found 1 error.

Also on Stack Overflow: Argument of type 'awaited T' is not assignable to parameter of type 'T'

Playground 3.9.0-dev.20200324

Playground Nightly

Playground 3.8.3

@Luxcium
Copy link
Author

Luxcium commented May 4, 2020

Due to different other problems in my code when using other TS version I am stuck using Version 3.9.0-dev.20200324

In regard to the problem described above in my previous comment I could, meanwhile, do:

return promise.then(t => funct((t as any) as T));

or do:

return promise.then(t => funct((t as unknown) as T));

I am not sure the difference between using unknown or any in this particular example ...


This is when using Version 4.0.0-dev.20200504 other parts of code are not shown here, the problem is detailed above in this current GitHub issue #37664

Many more problems using current version

@jakebailey
Copy link
Member

After 4.5, I don't think that this is a problem anymore thanks to the Awaited changes; I'm going to close this, but can reopen if I'm mistaken.

@Luxcium
Copy link
Author

Luxcium commented Jan 28, 2022

It's not a problem anymore indeed !!! thanks to the TS Team to have made it possible... and sorry for the long delay but at least it is now resolved for me !!!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Needs More Info The issue still hasn't been fully clarified
Projects
None yet
Development

No branches or pull requests

4 participants