TypeScript prevent use of Promise, errors on Awaited

Viewed 78

In the following sample, the I do not want the function foo accepting a Promise, but any other type should be OK.

export {}

function foo<T>(arg: T extends Promise<unknown> ? never : T) {
  console.log(arg);
}

async function bar<T>(arg: Promise<T>) {
  foo(await arg); // Why is this an error?
}

async function baz(arg: Promise<number>) {
  foo(await arg); // OK!
}

foo(await Promise.resolve(1)); // OK

foo(1); // OK

foo(Promise.resolve(1)); // Not OK, as intended

In the bar function, the error is:

Argument of type 'Awaited<T>' is not assignable
to parameter of type 'T extends Promise<unknown> ? never : T'.

What is the difference between T and Awaited<T>?

Is there any way to make this work as expected, when using await on a generic?

1 Answers

That type states if T extends Promise<unknown> then the argument must be of type never. But if we don't know what T is inside bar then we can not be sure that the type we pass to foo is correct.

In other words, it's technically possible to pass a parameter of type Promise<Promise<any>> to bar, in which case T is technically a Promise.

However, await resolves nested promises, so we know after await the resolved value can't be a Promise. The Typescript compiler does not seem to be able to pick up on this.

So you can create a generic type for this argument, and safely cast it after await.

type NotPromise<T> = T extends Promise<unknown> ? never: T;

function foo<T>(arg: NotPromise<T>) {
  console.log(arg);
}

async function bar<T>(arg: Promise<T>) {
  foo((await arg) as NotPromise<T>);
}

Working example: https://stackblitz.com/edit/typescript-h2izuv?file=index.ts

Related