Skip to content

Commit 46ce88c

Browse files
committed
Adds 'Awaited' type alias and updates to Promise.all/race/allSettled/any
1 parent 1f85123 commit 46ce88c

33 files changed

+1079
-125
lines changed

src/harness/fourslashInterfaceImpl.ts

+1
Original file line numberDiff line numberDiff line change
@@ -1079,6 +1079,7 @@ namespace FourSlashInterface {
10791079
typeEntry("PromiseConstructorLike"),
10801080
interfaceEntry("PromiseLike"),
10811081
interfaceEntry("Promise"),
1082+
typeEntry("Awaited"),
10821083
interfaceEntry("ArrayLike"),
10831084
typeEntry("Partial"),
10841085
typeEntry("Required"),

src/lib/es2015.promise.d.ts

+16
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,14 @@ interface PromiseConstructor {
1212
*/
1313
new <T>(executor: (resolve: (value: T | PromiseLike<T>) => void, reject: (reason?: any) => void) => void): Promise<T>;
1414

15+
/**
16+
* Creates a Promise that is resolved with an array of results when all of the provided Promises
17+
* resolve, or rejected when any Promise is rejected.
18+
* @param values An array of Promises.
19+
* @returns A new Promise.
20+
*/
21+
all<T extends readonly unknown[] | []>(values: T): Promise<{ -readonly [P in keyof T]: Awaited<T[P]> }>;
22+
1523
/**
1624
* Creates a Promise that is resolved with an array of results when all of the provided Promises
1725
* resolve, or rejected when any Promise is rejected.
@@ -95,6 +103,14 @@ interface PromiseConstructor {
95103
// see: lib.es2015.iterable.d.ts
96104
// all<T>(values: Iterable<T | PromiseLike<T>>): Promise<T[]>;
97105

106+
/**
107+
* Creates a Promise that is resolved or rejected when any of the provided Promises are resolved
108+
* or rejected.
109+
* @param values An array of Promises.
110+
* @returns A new Promise.
111+
*/
112+
race<T extends readonly unknown[] | []>(values: T): Promise<Awaited<T[number]>>;
113+
98114
/**
99115
* Creates a Promise that is resolved or rejected when any of the provided Promises are resolved
100116
* or rejected.

src/lib/es2020.promise.d.ts

+1-2
Original file line numberDiff line numberDiff line change
@@ -17,8 +17,7 @@ interface PromiseConstructor {
1717
* @param values An array of Promises.
1818
* @returns A new Promise.
1919
*/
20-
allSettled<T extends readonly unknown[] | readonly [unknown]>(values: T):
21-
Promise<{ -readonly [P in keyof T]: PromiseSettledResult<T[P] extends PromiseLike<infer U> ? U : T[P]> }>;
20+
allSettled<T extends readonly unknown[] | []>(values: T): Promise<{ -readonly [P in keyof T]: PromiseSettledResult<Awaited<T[P]>> }>;
2221

2322
/**
2423
* Creates a Promise that is resolved with an array of results when all

src/lib/es2021.promise.d.ts

+7
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,13 @@ declare var AggregateError: AggregateErrorConstructor;
1414
* Represents the completion of an asynchronous operation
1515
*/
1616
interface PromiseConstructor {
17+
/**
18+
* The any function returns a promise that is fulfilled by the first given promise to be fulfilled, or rejected with an AggregateError containing an array of rejection reasons if all of the given promises are rejected. It resolves all elements of the passed iterable to promises as it runs this algorithm.
19+
* @param values An array or iterable of Promises.
20+
* @returns A new Promise.
21+
*/
22+
any<T extends readonly any[] | []>(values: T): Promise<Awaited<T[number]>>;
23+
1724
/**
1825
* The any function returns a promise that is fulfilled by the first given promise to be fulfilled, or rejected with an AggregateError containing an array of rejection reasons if all of the given promises are rejected. It resolves all elements of the passed iterable to promises as it runs this algorithm.
1926
* @param values An array or iterable of Promises.

src/lib/es5.d.ts

+11
Original file line numberDiff line numberDiff line change
@@ -1440,6 +1440,17 @@ interface Promise<T> {
14401440
catch<TResult = never>(onrejected?: ((reason: any) => TResult | PromiseLike<TResult>) | undefined | null): Promise<T | TResult>;
14411441
}
14421442

1443+
/**
1444+
* Recursively unwraps the "awaited type" of a type. Non-promise "thenables" should resolve to `never`. This emulates the behavior of `await`.
1445+
*/
1446+
type Awaited<T> =
1447+
T extends null | undefined ? T : // special case for `null | undefined` when not in `--noImplicitAny` mode
1448+
T extends { then(onfulfilled: infer F): any } ? // thenable, extracts the first argument to `then()`
1449+
F extends ((value: infer V) => any) ? // if the argument to `then` is callable, extracts the argument
1450+
Awaited<V> : // recursively unwrap the value
1451+
never : // the argument to `then` was not callable.
1452+
T; // non-thenable
1453+
14431454
interface ArrayLike<T> {
14441455
readonly length: number;
14451456
readonly [n: number]: T;

tests/baselines/reference/asyncArrowFunction9_es2017.errors.txt

+1-1
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ tests/cases/conformance/async/es2017/asyncArrowFunction/asyncArrowFunction9_es20
1616
!!! error TS1005: ',' expected.
1717
~~~~~~~
1818
!!! error TS2403: Subsequent variable declarations must have the same type. Variable 'Promise' must be of type 'PromiseConstructor', but here has type 'any'.
19-
!!! related TS6203 /.ts/lib.es2015.promise.d.ts:150:13: 'Promise' was also declared here.
19+
!!! related TS6203 /.ts/lib.es2015.promise.d.ts:166:13: 'Promise' was also declared here.
2020
~
2121
!!! error TS1005: ',' expected.
2222
~~

tests/baselines/reference/asyncArrowFunction9_es5.errors.txt

+1-1
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ tests/cases/conformance/async/es5/asyncArrowFunction/asyncArrowFunction9_es5.ts(
1616
!!! error TS1005: ',' expected.
1717
~~~~~~~
1818
!!! error TS2403: Subsequent variable declarations must have the same type. Variable 'Promise' must be of type 'PromiseConstructor', but here has type 'any'.
19-
!!! related TS6203 /.ts/lib.es2015.promise.d.ts:150:13: 'Promise' was also declared here.
19+
!!! related TS6203 /.ts/lib.es2015.promise.d.ts:166:13: 'Promise' was also declared here.
2020
~
2121
!!! error TS1005: ',' expected.
2222
~~

tests/baselines/reference/asyncArrowFunction9_es6.errors.txt

+1-1
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ tests/cases/conformance/async/es6/asyncArrowFunction/asyncArrowFunction9_es6.ts(
1616
!!! error TS1005: ',' expected.
1717
~~~~~~~
1818
!!! error TS2403: Subsequent variable declarations must have the same type. Variable 'Promise' must be of type 'PromiseConstructor', but here has type 'any'.
19-
!!! related TS6203 /.ts/lib.es2015.promise.d.ts:150:13: 'Promise' was also declared here.
19+
!!! related TS6203 /.ts/lib.es2015.promise.d.ts:166:13: 'Promise' was also declared here.
2020
~
2121
!!! error TS1005: ',' expected.
2222
~~
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
tests/cases/compiler/awaitedType.ts(18,12): error TS2589: Type instantiation is excessively deep and possibly infinite.
2+
tests/cases/compiler/awaitedType.ts(22,12): error TS2589: Type instantiation is excessively deep and possibly infinite.
3+
4+
5+
==== tests/cases/compiler/awaitedType.ts (2 errors) ====
6+
type T1 = Awaited<number>;
7+
type T2 = Awaited<Promise<number>>;
8+
type T3 = Awaited<number | Promise<number>>;
9+
type T4 = Awaited<number | Promise<string>>;
10+
type T5 = Awaited<{ then: number }>;
11+
type T6 = Awaited<{ then(): void }>; // never (non-promise "thenable")
12+
type T7 = Awaited<{ then(x: number): void }>; // never (non-promise "thenable")
13+
type T8 = Awaited<{ then(x: () => void): void }>; // unknown
14+
type T9 = Awaited<any>;
15+
type T10 = Awaited<never>;
16+
type T11 = Awaited<unknown>;
17+
type T12 = Awaited<Promise<Promise<number>>>;
18+
type T13 = _Expect<Awaited<Promise<Promise<number>> | string | null>, /*expected*/ string | number | null>; // otherwise just prints T13 in types tests, which isn't very helpful
19+
type T14 = _Expect<Awaited<Promise<Promise<number>> | string | undefined>, /*expected*/ string | number | undefined>; // otherwise just prints T14 in types tests, which isn't very helpful
20+
type T15 = _Expect<Awaited<Promise<Promise<number>> | string | null | undefined>, /*expected*/ string | number | null | undefined>; // otherwise just prints T15 in types tests, which isn't very helpful
21+
22+
interface BadPromise { then(cb: (value: BadPromise) => void): void; }
23+
type T16 = Awaited<BadPromise>; // error
24+
~~~~~~~~~~~~~~~~~~~
25+
!!! error TS2589: Type instantiation is excessively deep and possibly infinite.
26+
27+
interface BadPromise1 { then(cb: (value: BadPromise2) => void): void; }
28+
interface BadPromise2 { then(cb: (value: BadPromise1) => void): void; }
29+
type T17 = Awaited<BadPromise1>; // error
30+
~~~~~~~~~~~~~~~~~~~~
31+
!!! error TS2589: Type instantiation is excessively deep and possibly infinite.
32+
33+
// https://github.com/microsoft/TypeScript/issues/33562
34+
type MaybePromise<T> = T | Promise<T> | PromiseLike<T>
35+
declare function MaybePromise<T>(value: T): MaybePromise<T>;
36+
37+
async function main() {
38+
let aaa: number;
39+
let bbb: string;
40+
[
41+
aaa,
42+
bbb,
43+
] = await Promise.all([
44+
MaybePromise(1),
45+
MaybePromise('2'),
46+
MaybePromise(true),
47+
])
48+
}
49+
50+
// helps with tests where '.types' just prints out the type alias name
51+
type _Expect<TActual extends TExpected, TExpected> = TActual;
52+
+58
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,58 @@
1+
//// [awaitedType.ts]
2+
type T1 = Awaited<number>;
3+
type T2 = Awaited<Promise<number>>;
4+
type T3 = Awaited<number | Promise<number>>;
5+
type T4 = Awaited<number | Promise<string>>;
6+
type T5 = Awaited<{ then: number }>;
7+
type T6 = Awaited<{ then(): void }>; // never (non-promise "thenable")
8+
type T7 = Awaited<{ then(x: number): void }>; // never (non-promise "thenable")
9+
type T8 = Awaited<{ then(x: () => void): void }>; // unknown
10+
type T9 = Awaited<any>;
11+
type T10 = Awaited<never>;
12+
type T11 = Awaited<unknown>;
13+
type T12 = Awaited<Promise<Promise<number>>>;
14+
type T13 = _Expect<Awaited<Promise<Promise<number>> | string | null>, /*expected*/ string | number | null>; // otherwise just prints T13 in types tests, which isn't very helpful
15+
type T14 = _Expect<Awaited<Promise<Promise<number>> | string | undefined>, /*expected*/ string | number | undefined>; // otherwise just prints T14 in types tests, which isn't very helpful
16+
type T15 = _Expect<Awaited<Promise<Promise<number>> | string | null | undefined>, /*expected*/ string | number | null | undefined>; // otherwise just prints T15 in types tests, which isn't very helpful
17+
18+
interface BadPromise { then(cb: (value: BadPromise) => void): void; }
19+
type T16 = Awaited<BadPromise>; // error
20+
21+
interface BadPromise1 { then(cb: (value: BadPromise2) => void): void; }
22+
interface BadPromise2 { then(cb: (value: BadPromise1) => void): void; }
23+
type T17 = Awaited<BadPromise1>; // error
24+
25+
// https://github.com/microsoft/TypeScript/issues/33562
26+
type MaybePromise<T> = T | Promise<T> | PromiseLike<T>
27+
declare function MaybePromise<T>(value: T): MaybePromise<T>;
28+
29+
async function main() {
30+
let aaa: number;
31+
let bbb: string;
32+
[
33+
aaa,
34+
bbb,
35+
] = await Promise.all([
36+
MaybePromise(1),
37+
MaybePromise('2'),
38+
MaybePromise(true),
39+
])
40+
}
41+
42+
// helps with tests where '.types' just prints out the type alias name
43+
type _Expect<TActual extends TExpected, TExpected> = TActual;
44+
45+
46+
//// [awaitedType.js]
47+
async function main() {
48+
let aaa;
49+
let bbb;
50+
[
51+
aaa,
52+
bbb,
53+
] = await Promise.all([
54+
MaybePromise(1),
55+
MaybePromise('2'),
56+
MaybePromise(true),
57+
]);
58+
}

0 commit comments

Comments
 (0)