Skip to content

Commit 9c6c247

Browse files
committed
Defer generic awaited type
1 parent 08d58d7 commit 9c6c247

File tree

64 files changed

+562
-595
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

64 files changed

+562
-595
lines changed

src/compiler/checker.ts

+15-87
Original file line numberDiff line numberDiff line change
@@ -799,6 +799,7 @@ namespace ts {
799799
let deferredGlobalESSymbolConstructorSymbol: Symbol | undefined;
800800
let deferredGlobalESSymbolType: ObjectType;
801801
let deferredGlobalTypedPropertyDescriptorType: GenericType;
802+
let deferredGlobalAwaitedSymbol: Symbol | undefined;
802803
let deferredGlobalPromiseType: GenericType;
803804
let deferredGlobalPromiseLikeType: GenericType;
804805
let deferredGlobalPromiseConstructorSymbol: Symbol | undefined;
@@ -852,7 +853,6 @@ namespace ts {
852853
const flowNodeReachable: (boolean | undefined)[] = [];
853854
const potentialThisCollisions: Node[] = [];
854855
const potentialNewTargetCollisions: Node[] = [];
855-
const awaitedTypeStack: number[] = [];
856856

857857
const diagnostics = createDiagnosticCollection();
858858
const suggestionDiagnostics = createDiagnosticCollection();
@@ -11121,6 +11121,10 @@ namespace ts {
1112111121
return deferredGlobalESSymbolType || (deferredGlobalESSymbolType = getGlobalType("Symbol" as __String, /*arity*/ 0, reportErrors)) || emptyObjectType;
1112211122
}
1112311123

11124+
function getGlobalAwaitedSymbol(reportErrors: boolean) {
11125+
return deferredGlobalAwaitedSymbol || (deferredGlobalAwaitedSymbol = getGlobalTypeSymbol("Awaited" as __String, reportErrors));
11126+
}
11127+
1112411128
function getGlobalPromiseType(reportErrors: boolean) {
1112511129
return deferredGlobalPromiseType || (deferredGlobalPromiseType = getGlobalType("Promise" as __String, /*arity*/ 1, reportErrors)) || emptyGenericType;
1112611130
}
@@ -29100,98 +29104,22 @@ namespace ts {
2910029104
return typeAsAwaitable.awaitedTypeOfType = type;
2910129105
}
2910229106

29103-
if (type.flags & TypeFlags.Union) {
29104-
let types: Type[] | undefined;
29105-
for (const constituentType of (<UnionType>type).types) {
29106-
types = append<Type>(types, getAwaitedType(constituentType, errorNode, diagnosticMessage, arg0));
29107-
}
29108-
29109-
if (!types) {
29110-
return undefined;
29111-
}
29112-
29113-
return typeAsAwaitable.awaitedTypeOfType = getUnionType(types);
29107+
const symbol = getGlobalAwaitedSymbol(/*reportErrors*/ false);
29108+
if (!symbol) {
29109+
return typeAsAwaitable.awaitedTypeOfType = type;
2911429110
}
2911529111

29116-
const promisedType = getPromisedTypeOfPromise(type);
29117-
if (promisedType) {
29118-
if (type.id === promisedType.id || awaitedTypeStack.indexOf(promisedType.id) >= 0) {
29119-
// Verify that we don't have a bad actor in the form of a promise whose
29120-
// promised type is the same as the promise type, or a mutually recursive
29121-
// promise. If so, we return undefined as we cannot guess the shape. If this
29122-
// were the actual case in the JavaScript, this Promise would never resolve.
29123-
//
29124-
// An example of a bad actor with a singly-recursive promise type might
29125-
// be:
29126-
//
29127-
// interface BadPromise {
29128-
// then(
29129-
// onfulfilled: (value: BadPromise) => any,
29130-
// onrejected: (error: any) => any): BadPromise;
29131-
// }
29132-
// The above interface will pass the PromiseLike check, and return a
29133-
// promised type of `BadPromise`. Since this is a self reference, we
29134-
// don't want to keep recursing ad infinitum.
29135-
//
29136-
// An example of a bad actor in the form of a mutually-recursive
29137-
// promise type might be:
29138-
//
29139-
// interface BadPromiseA {
29140-
// then(
29141-
// onfulfilled: (value: BadPromiseB) => any,
29142-
// onrejected: (error: any) => any): BadPromiseB;
29143-
// }
29144-
//
29145-
// interface BadPromiseB {
29146-
// then(
29147-
// onfulfilled: (value: BadPromiseA) => any,
29148-
// onrejected: (error: any) => any): BadPromiseA;
29149-
// }
29150-
//
29151-
if (errorNode) {
29152-
error(errorNode, Diagnostics.Type_is_referenced_directly_or_indirectly_in_the_fulfillment_callback_of_its_own_then_method);
29153-
}
29154-
return undefined;
29155-
}
29156-
29157-
// Keep track of the type we're about to unwrap to avoid bad recursive promise types.
29158-
// See the comments above for more information.
29159-
awaitedTypeStack.push(type.id);
29160-
const awaitedType = getAwaitedType(promisedType, errorNode, diagnosticMessage, arg0);
29161-
awaitedTypeStack.pop();
29162-
29163-
if (!awaitedType) {
29164-
return undefined;
29165-
}
29166-
29167-
return typeAsAwaitable.awaitedTypeOfType = awaitedType;
29112+
const result = getTypeAliasInstantiation(symbol, [type]);
29113+
if (result !== unknownType || type === unknownType || getPromisedTypeOfPromise(type) === unknownType) {
29114+
return typeAsAwaitable.awaitedTypeOfType = (result as PromiseOrAwaitableType).awaitedTypeOfType = result;
2916829115
}
2916929116

29170-
// The type was not a promise, so it could not be unwrapped any further.
29171-
// As long as the type does not have a callable "then" property, it is
29172-
// safe to return the type; otherwise, an error will be reported in
29173-
// the call to getNonThenableType and we will return undefined.
29174-
//
29175-
// An example of a non-promise "thenable" might be:
29176-
//
29177-
// await { then(): void {} }
29178-
//
29179-
// The "thenable" does not match the minimal definition for a promise. When
29180-
// a Promise/A+-compatible or ES6 promise tries to adopt this value, the promise
29181-
// will never settle. We treat this as an error to help flag an early indicator
29182-
// of a runtime problem. If the user wants to return this value from an async
29183-
// function, they would need to wrap it in some other value. If they want it to
29184-
// be treated as a promise, they can cast to <any>.
29185-
const thenFunction = getTypeOfPropertyOfType(type, "then" as __String);
29186-
if (thenFunction && getSignaturesOfType(thenFunction, SignatureKind.Call).length > 0) {
29187-
if (errorNode) {
29188-
if (!diagnosticMessage) return Debug.fail();
29189-
error(errorNode, diagnosticMessage, arg0);
29190-
}
29191-
return undefined;
29117+
if (errorNode) {
29118+
if (!diagnosticMessage) return Debug.fail();
29119+
error(errorNode, diagnosticMessage, arg0);
2919229120
}
2919329121

29194-
return typeAsAwaitable.awaitedTypeOfType = type;
29122+
return undefined;
2919529123
}
2919629124

2919729125
/**

src/harness/fourslashInterfaceImpl.ts

+1
Original file line numberDiff line numberDiff line change
@@ -953,6 +953,7 @@ namespace FourSlashInterface {
953953
typeEntry("PropertyDecorator"),
954954
typeEntry("MethodDecorator"),
955955
typeEntry("ParameterDecorator"),
956+
typeEntry("Awaited"),
956957
typeEntry("PromiseConstructorLike"),
957958
interfaceEntry("PromiseLike"),
958959
interfaceEntry("Promise"),

src/lib/es2015.iterable.d.ts

+3-3
Original file line numberDiff line numberDiff line change
@@ -200,18 +200,18 @@ interface PromiseConstructor {
200200
/**
201201
* Creates a Promise that is resolved with an array of results when all of the provided Promises
202202
* resolve, or rejected when any Promise is rejected.
203-
* @param values An array of Promises.
203+
* @param values An iterable of Promises.
204204
* @returns A new Promise.
205205
*/
206-
all<TAll>(values: Iterable<TAll | PromiseLike<TAll>>): Promise<TAll[]>;
206+
all<TAll>(values: Iterable<TAll>): Promise<Awaited<TAll>[]>;
207207

208208
/**
209209
* Creates a Promise that is resolved or rejected when any of the provided Promises are resolved
210210
* or rejected.
211211
* @param values An iterable of Promises.
212212
* @returns A new Promise.
213213
*/
214-
race<T>(values: Iterable<T>): Promise<T extends PromiseLike<infer U> ? U : T>;
214+
race<T>(values: Iterable<T>): Promise<Awaited<T>>;
215215
}
216216

217217
declare namespace Reflect {

src/lib/es2015.promise.d.ts

+5-77
Original file line numberDiff line numberDiff line change
@@ -10,95 +10,23 @@ interface PromiseConstructor {
1010
* a resolve callback used to resolve the promise with a value or the result of another promise,
1111
* and a reject callback used to reject the promise with a provided reason or error.
1212
*/
13-
new <T>(executor: (resolve: (value?: T | PromiseLike<T>) => void, reject: (reason?: any) => void) => void): Promise<T>;
13+
new <T>(executor: (resolve: (value?: T) => void, reject: (reason?: any) => void) => void): Promise<Awaited<T>>;
1414

1515
/**
1616
* Creates a Promise that is resolved with an array of results when all of the provided Promises
1717
* resolve, or rejected when any Promise is rejected.
1818
* @param values An array of Promises.
1919
* @returns A new Promise.
2020
*/
21-
all<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10>(values: readonly [T1 | PromiseLike<T1>, T2 | PromiseLike<T2>, T3 | PromiseLike<T3>, T4 | PromiseLike <T4>, T5 | PromiseLike<T5>, T6 | PromiseLike<T6>, T7 | PromiseLike<T7>, T8 | PromiseLike<T8>, T9 | PromiseLike<T9>, T10 | PromiseLike<T10>]): Promise<[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10]>;
22-
23-
/**
24-
* Creates a Promise that is resolved with an array of results when all of the provided Promises
25-
* resolve, or rejected when any Promise is rejected.
26-
* @param values An array of Promises.
27-
* @returns A new Promise.
28-
*/
29-
all<T1, T2, T3, T4, T5, T6, T7, T8, T9>(values: readonly [T1 | PromiseLike<T1>, T2 | PromiseLike<T2>, T3 | PromiseLike<T3>, T4 | PromiseLike <T4>, T5 | PromiseLike<T5>, T6 | PromiseLike<T6>, T7 | PromiseLike<T7>, T8 | PromiseLike<T8>, T9 | PromiseLike<T9>]): Promise<[T1, T2, T3, T4, T5, T6, T7, T8, T9]>;
30-
31-
/**
32-
* Creates a Promise that is resolved with an array of results when all of the provided Promises
33-
* resolve, or rejected when any Promise is rejected.
34-
* @param values An array of Promises.
35-
* @returns A new Promise.
36-
*/
37-
all<T1, T2, T3, T4, T5, T6, T7, T8>(values: readonly [T1 | PromiseLike<T1>, T2 | PromiseLike<T2>, T3 | PromiseLike<T3>, T4 | PromiseLike <T4>, T5 | PromiseLike<T5>, T6 | PromiseLike<T6>, T7 | PromiseLike<T7>, T8 | PromiseLike<T8>]): Promise<[T1, T2, T3, T4, T5, T6, T7, T8]>;
38-
39-
/**
40-
* Creates a Promise that is resolved with an array of results when all of the provided Promises
41-
* resolve, or rejected when any Promise is rejected.
42-
* @param values An array of Promises.
43-
* @returns A new Promise.
44-
*/
45-
all<T1, T2, T3, T4, T5, T6, T7>(values: readonly [T1 | PromiseLike<T1>, T2 | PromiseLike<T2>, T3 | PromiseLike<T3>, T4 | PromiseLike <T4>, T5 | PromiseLike<T5>, T6 | PromiseLike<T6>, T7 | PromiseLike<T7>]): Promise<[T1, T2, T3, T4, T5, T6, T7]>;
46-
47-
/**
48-
* Creates a Promise that is resolved with an array of results when all of the provided Promises
49-
* resolve, or rejected when any Promise is rejected.
50-
* @param values An array of Promises.
51-
* @returns A new Promise.
52-
*/
53-
all<T1, T2, T3, T4, T5, T6>(values: readonly [T1 | PromiseLike<T1>, T2 | PromiseLike<T2>, T3 | PromiseLike<T3>, T4 | PromiseLike <T4>, T5 | PromiseLike<T5>, T6 | PromiseLike<T6>]): Promise<[T1, T2, T3, T4, T5, T6]>;
54-
55-
/**
56-
* Creates a Promise that is resolved with an array of results when all of the provided Promises
57-
* resolve, or rejected when any Promise is rejected.
58-
* @param values An array of Promises.
59-
* @returns A new Promise.
60-
*/
61-
all<T1, T2, T3, T4, T5>(values: readonly [T1 | PromiseLike<T1>, T2 | PromiseLike<T2>, T3 | PromiseLike<T3>, T4 | PromiseLike <T4>, T5 | PromiseLike<T5>]): Promise<[T1, T2, T3, T4, T5]>;
62-
63-
/**
64-
* Creates a Promise that is resolved with an array of results when all of the provided Promises
65-
* resolve, or rejected when any Promise is rejected.
66-
* @param values An array of Promises.
67-
* @returns A new Promise.
68-
*/
69-
all<T1, T2, T3, T4>(values: readonly [T1 | PromiseLike<T1>, T2 | PromiseLike<T2>, T3 | PromiseLike<T3>, T4 | PromiseLike <T4>]): Promise<[T1, T2, T3, T4]>;
70-
71-
/**
72-
* Creates a Promise that is resolved with an array of results when all of the provided Promises
73-
* resolve, or rejected when any Promise is rejected.
74-
* @param values An array of Promises.
75-
* @returns A new Promise.
76-
*/
77-
all<T1, T2, T3>(values: readonly [T1 | PromiseLike<T1>, T2 | PromiseLike<T2>, T3 | PromiseLike<T3>]): Promise<[T1, T2, T3]>;
78-
79-
/**
80-
* Creates a Promise that is resolved with an array of results when all of the provided Promises
81-
* resolve, or rejected when any Promise is rejected.
82-
* @param values An array of Promises.
83-
* @returns A new Promise.
84-
*/
85-
all<T1, T2>(values: readonly [T1 | PromiseLike<T1>, T2 | PromiseLike<T2>]): Promise<[T1, T2]>;
86-
87-
/**
88-
* Creates a Promise that is resolved with an array of results when all of the provided Promises
89-
* resolve, or rejected when any Promise is rejected.
90-
* @param values An array of Promises.
91-
* @returns A new Promise.
92-
*/
93-
all<T>(values: readonly (T | PromiseLike<T>)[]): Promise<T[]>;
21+
all<T extends readonly any[]>(values: T): Promise<{ -readonly [P in keyof T]: Awaited<T[P]> }>;
9422

9523
/**
9624
* Creates a Promise that is resolved or rejected when any of the provided Promises are resolved
9725
* or rejected.
9826
* @param values An array of Promises.
9927
* @returns A new Promise.
10028
*/
101-
race<T>(values: readonly T[]): Promise<T extends PromiseLike<infer U> ? U : T>;
29+
race<T extends readonly any[]>(values: T): Promise<Awaited<T[number]>>;
10230

10331
/**
10432
* Creates a new rejected promise for the provided reason.
@@ -112,10 +40,10 @@ interface PromiseConstructor {
11240
* @param value A promise.
11341
* @returns A promise whose internal state matches the provided promise.
11442
*/
115-
resolve<T>(value: T | PromiseLike<T>): Promise<T>;
43+
resolve<T>(value: T): Promise<Awaited<T>>;
11644

11745
/**
118-
* Creates a new resolved promise .
46+
* Creates a new resolved promise.
11947
* @returns A resolved promise.
12048
*/
12149
resolve(): Promise<void>;

src/lib/es2018.asyncgenerator.d.ts

+3-3
Original file line numberDiff line numberDiff line change
@@ -2,9 +2,9 @@
22

33
interface AsyncGenerator<T = unknown, TReturn = any, TNext = unknown> extends AsyncIterator<T, TReturn, TNext> {
44
// NOTE: 'next' is defined using a tuple to ensure we report the correct assignability errors in all places.
5-
next(...args: [] | [TNext]): Promise<IteratorResult<T, TReturn>>;
6-
return(value: TReturn | PromiseLike<TReturn>): Promise<IteratorResult<T, TReturn>>;
7-
throw(e: any): Promise<IteratorResult<T, TReturn>>;
5+
next(...args: [] | [TNext]): Promise<IteratorResult<T, Awaited<TReturn>>>;
6+
return(value: TReturn): Promise<IteratorResult<T, Awaited<TReturn>>>;
7+
throw(e: any): Promise<IteratorResult<T, Awaited<TReturn>>>;
88
[Symbol.asyncIterator](): AsyncGenerator<T, TReturn, TNext>;
99
}
1010

src/lib/es2018.asynciterable.d.ts

+4-4
Original file line numberDiff line numberDiff line change
@@ -11,9 +11,9 @@ interface SymbolConstructor {
1111

1212
interface AsyncIterator<T, TReturn = any, TNext = undefined> {
1313
// NOTE: 'next' is defined using a tuple to ensure we report the correct assignability errors in all places.
14-
next(...args: [] | [TNext]): Promise<IteratorResult<T, TReturn>>;
15-
return?(value?: TReturn | PromiseLike<TReturn>): Promise<IteratorResult<T, TReturn>>;
16-
throw?(e?: any): Promise<IteratorResult<T, TReturn>>;
14+
next(...args: [] | [TNext]): Promise<IteratorResult<T, Awaited<TReturn>>>;
15+
return?(value?: TReturn): Promise<IteratorResult<T, Awaited<TReturn>>>;
16+
throw?(e?: any): Promise<IteratorResult<T, Awaited<TReturn>>>;
1717
}
1818

1919
interface AsyncIterable<T> {
@@ -22,4 +22,4 @@ interface AsyncIterable<T> {
2222

2323
interface AsyncIterableIterator<T> extends AsyncIterator<T> {
2424
[Symbol.asyncIterator](): AsyncIterableIterator<T>;
25-
}
25+
}

src/lib/es5.d.ts

+6-1
Original file line numberDiff line numberDiff line change
@@ -1378,7 +1378,12 @@ declare type PropertyDecorator = (target: Object, propertyKey: string | symbol)
13781378
declare type MethodDecorator = <T>(target: Object, propertyKey: string | symbol, descriptor: TypedPropertyDescriptor<T>) => TypedPropertyDescriptor<T> | void;
13791379
declare type ParameterDecorator = (target: Object, propertyKey: string | symbol, parameterIndex: number) => void;
13801380

1381-
declare type PromiseConstructorLike = new <T>(executor: (resolve: (value?: T | PromiseLike<T>) => void, reject: (reason?: any) => void) => void) => PromiseLike<T>;
1381+
// The undefined case is for strictNullChecks false, in which case
1382+
// undefined extends PromiseLike<infer U> is true, which would otherwise
1383+
// make Awaited<undefined> -> unknown.
1384+
type Awaited<T> = T extends undefined ? T : T extends PromiseLike<infer U> ? U : T extends { then(...args: any[]): any } ? unknown : T;
1385+
1386+
declare type PromiseConstructorLike = new <T>(executor: (resolve: (value?: T) => void, reject: (reason?: any) => void) => void) => PromiseLike<Awaited<T>>;
13821387

13831388
interface PromiseLike<T> {
13841389
/**

tests/baselines/reference/asyncArrowFunction11_es5.types

+2-2
Original file line numberDiff line numberDiff line change
@@ -11,9 +11,9 @@ class A {
1111
await Promise.resolve();
1212
>await Promise.resolve() : void
1313
>Promise.resolve() : Promise<void>
14-
>Promise.resolve : { <T>(value: T | PromiseLike<T>): Promise<T>; (): Promise<void>; }
14+
>Promise.resolve : { <T>(value: T): Promise<Awaited<T>>; (): Promise<void>; }
1515
>Promise : PromiseConstructor
16-
>resolve : { <T>(value: T | PromiseLike<T>): Promise<T>; (): Promise<void>; }
16+
>resolve : { <T>(value: T): Promise<Awaited<T>>; (): Promise<void>; }
1717

1818
const obj = { ["a"]: () => this }; // computed property name after `await` triggers case
1919
>obj : { a: () => this; }

tests/baselines/reference/asyncArrowFunction5_es2017.errors.txt

+1-1
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ tests/cases/conformance/async/es2017/asyncArrowFunction/asyncArrowFunction5_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:144:13: 'Promise' was also declared here.
19+
!!! related TS6203 /.ts/lib.es2015.promise.d.ts:72:13: 'Promise' was also declared here.
2020
~
2121
!!! error TS1005: ',' expected.
2222
~~

tests/baselines/reference/asyncArrowFunction5_es5.errors.txt

+1-1
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ tests/cases/conformance/async/es5/asyncArrowFunction/asyncArrowFunction5_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:144:13: 'Promise' was also declared here.
19+
!!! related TS6203 /.ts/lib.es2015.promise.d.ts:72:13: 'Promise' was also declared here.
2020
~
2121
!!! error TS1005: ',' expected.
2222
~~

tests/baselines/reference/asyncArrowFunction5_es6.errors.txt

+1-1
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ tests/cases/conformance/async/es6/asyncArrowFunction/asyncArrowFunction5_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:144:13: 'Promise' was also declared here.
19+
!!! related TS6203 /.ts/lib.es2015.promise.d.ts:72:13: 'Promise' was also declared here.
2020
~
2121
!!! error TS1005: ',' expected.
2222
~~

0 commit comments

Comments
 (0)