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

Can't infer T from awaited T #37526

Closed
falsandtru opened this issue Mar 23, 2020 · 3 comments · Fixed by #37610
Closed

Can't infer T from awaited T #37526

falsandtru opened this issue Mar 23, 2020 · 3 comments · Fixed by #37610
Assignees
Labels
Needs Investigation This issue needs a team member to investigate its status.

Comments

@falsandtru
Copy link
Contributor

Looks like resolving this problem by type inference is difficult. We may need unawaited operator.

TypeScript Version: 3.9.0-dev.20200322

Search Terms:

Expected behavior:
pass
Actual behavior:

Argument of type 'T' is not assignable to parameter of type 'awaited T | PromiseLike<awaited T> | undefined'.
  Type 'T' is not assignable to type 'PromiseLike<awaited T>'.(2345)

Related Issues:

Code

function f<T>(a: T): Promise<awaited T> {
    return new Promise(r => r(a));
}
Output
"use strict";
function f(a) {
    return new Promise(r => r(a));
}
Compiler Options
{
  "compilerOptions": {
    "noImplicitAny": true,
    "strictNullChecks": true,
    "strictFunctionTypes": true,
    "strictPropertyInitialization": true,
    "strictBindCallApply": true,
    "noImplicitThis": true,
    "noImplicitReturns": true,
    "alwaysStrict": true,
    "esModuleInterop": true,
    "declaration": true,
    "experimentalDecorators": true,
    "emitDecoratorMetadata": true,
    "moduleResolution": 2,
    "target": "ES2017",
    "jsx": "React",
    "module": "ESNext"
  }
}

Playground Link: Provided

@rbuckton
Copy link
Member

We don't necessarily need an unawaited operator. Since Promise<T> is assignable to Promise<awaited T>, we can change inference to infer Promise<T> in this scenario.

@Luxcium
Copy link

Luxcium commented Mar 24, 2020

Update

My problem is solved with the nightly version of typescript but I will leave it there in case it can be useful for something or someone

"typescript": "3.9.0-dev.20200324"

I have placed a copy of this now fixed problem in #33055 and in #33707 hoping it can be helpful and not too spammy ...

Issue now fixed

I have a similar problem and I don't know if I should open a different issue ...

"typescript": "3.8.3"

    // 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»

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

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

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

/*
 // 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); will display same as console.log(powersOneToTen);



// console.log(powersOneToTen); ->
// "log->" powersOneToTen: MyMaybeList<Promise<number>>
MyMaybeList {
  values: [
    Promise { 1 },
    Promise { 4 },
    Promise { 9 },
    Promise { 16 },
    Promise { 25 },
    Promise { 36 },
    Promise { 49 },
    Promise { 64 },
    Promise { 81 },
    Promise { 100 }
  ]
}



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

// awaitedPowersOneToTen.then(awaitedList => console.log(awaitedList)); ->
// console.log(awaitedList) ->
// "log->" awaitedList: MyMaybeList<Promise<number>>
// instead of a -> MyMaybeList { Promise<number> }
// in fact is a (should be) -> Promise {  MyMaybeList<number> }
MyMaybeList {
  values: [
     1,  4,  9, 16,  25,
    36, 49, 64, 81, 100
  ]
}
*/

@Luxcium
Copy link

Luxcium commented Mar 27, 2020

Now that #37610 has reverted the awaited type I don't know what to do I have posted 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[]>>>)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Needs Investigation This issue needs a team member to investigate its status.
Projects
None yet
4 participants