Skip to content

Commit 9bd9414

Browse files
committed
Defer generic awaited type
1 parent eed9402 commit 9bd9414

File tree

101 files changed

+1449
-1545
lines changed

Some content is hidden

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

101 files changed

+1449
-1545
lines changed

src/compiler/checker.ts

+26-109
Original file line numberDiff line numberDiff line change
@@ -800,6 +800,7 @@ namespace ts {
800800
let deferredGlobalESSymbolConstructorSymbol: Symbol | undefined;
801801
let deferredGlobalESSymbolType: ObjectType;
802802
let deferredGlobalTypedPropertyDescriptorType: GenericType;
803+
let deferredGlobalAwaitedSymbol: Symbol | undefined;
803804
let deferredGlobalPromiseType: GenericType;
804805
let deferredGlobalPromiseLikeType: GenericType;
805806
let deferredGlobalPromiseConstructorSymbol: Symbol | undefined;
@@ -853,7 +854,6 @@ namespace ts {
853854
const flowNodeReachable: (boolean | undefined)[] = [];
854855
const potentialThisCollisions: Node[] = [];
855856
const potentialNewTargetCollisions: Node[] = [];
856-
const awaitedTypeStack: number[] = [];
857857

858858
const diagnostics = createDiagnosticCollection();
859859
const suggestionDiagnostics = createDiagnosticCollection();
@@ -11059,6 +11059,10 @@ namespace ts {
1105911059
return deferredGlobalESSymbolType || (deferredGlobalESSymbolType = getGlobalType("Symbol" as __String, /*arity*/ 0, reportErrors)) || emptyObjectType;
1106011060
}
1106111061

11062+
function getGlobalAwaitedSymbol(reportErrors: boolean) {
11063+
return deferredGlobalAwaitedSymbol || (deferredGlobalAwaitedSymbol = getGlobalTypeSymbol("Awaited" as __String, reportErrors));
11064+
}
11065+
1106211066
function getGlobalPromiseType(reportErrors: boolean) {
1106311067
return deferredGlobalPromiseType || (deferredGlobalPromiseType = getGlobalType("Promise" as __String, /*arity*/ 1, reportErrors)) || emptyGenericType;
1106411068
}
@@ -25719,7 +25723,7 @@ namespace ts {
2571925723
// Promise/A+ compatible implementation will always assimilate any foreign promise, so the
2572025724
// return type of the body should be unwrapped to its awaited type, which we will wrap in
2572125725
// the native Promise<T> type later in this function.
25722-
returnType = checkAwaitedType(returnType, /*errorNode*/ func, Diagnostics.The_return_type_of_an_async_function_must_either_be_a_valid_promise_or_must_not_contain_a_callable_then_member);
25726+
returnType = checkAwaitedType(returnType);
2572325727
}
2572425728
}
2572525729
else if (isGenerator) { // Generator or AsyncGenerator function
@@ -25858,9 +25862,7 @@ namespace ts {
2585825862
const errorNode = node.expression || node;
2585925863
// A `yield*` expression effectively yields everything that its operand yields
2586025864
const yieldedType = node.asteriskToken ? checkIteratedTypeOrElementType(isAsync ? IterationUse.AsyncYieldStar : IterationUse.YieldStar, expressionType, sentType, errorNode) : expressionType;
25861-
return !isAsync ? yieldedType : getAwaitedType(yieldedType, errorNode, node.asteriskToken
25862-
? Diagnostics.Type_of_iterated_elements_of_a_yield_Asterisk_operand_must_either_be_a_valid_promise_or_must_not_contain_a_callable_then_member
25863-
: Diagnostics.Type_of_yield_operand_in_an_async_generator_must_either_be_a_valid_promise_or_must_not_contain_a_callable_then_member);
25865+
return !isAsync ? yieldedType : getAwaitedType(yieldedType);
2586425866
}
2586525867

2586625868
/**
@@ -25951,7 +25953,7 @@ namespace ts {
2595125953
// Promise/A+ compatible implementation will always assimilate any foreign promise, so the
2595225954
// return type of the body should be unwrapped to its awaited type, which should be wrapped in
2595325955
// the native Promise<T> type by the caller.
25954-
type = checkAwaitedType(type, func, Diagnostics.The_return_type_of_an_async_function_must_either_be_a_valid_promise_or_must_not_contain_a_callable_then_member);
25956+
type = checkAwaitedType(type);
2595525957
}
2595625958
if (type.flags & TypeFlags.Never) {
2595725959
hasReturnOfTypeNever = true;
@@ -26155,7 +26157,7 @@ namespace ts {
2615526157
const returnOrPromisedType = getReturnOrPromisedType(returnType, functionFlags);
2615626158
if (returnOrPromisedType) {
2615726159
if ((functionFlags & FunctionFlags.AsyncGenerator) === FunctionFlags.Async) { // Async function
26158-
const awaitedType = checkAwaitedType(exprType, node.body, Diagnostics.The_return_type_of_an_async_function_must_either_be_a_valid_promise_or_must_not_contain_a_callable_then_member);
26160+
const awaitedType = checkAwaitedType(exprType);
2615926161
checkTypeAssignableToAndOptionallyElaborate(awaitedType, returnOrPromisedType, node.body, node.body);
2616026162
}
2616126163
else { // Normal function
@@ -26324,7 +26326,7 @@ namespace ts {
2632426326
}
2632526327

2632626328
const operandType = checkExpression(node.expression);
26327-
const awaitedType = checkAwaitedType(operandType, node, Diagnostics.Type_of_await_operand_must_either_be_a_valid_promise_or_must_not_contain_a_callable_then_member);
26329+
const awaitedType = checkAwaitedType(operandType);
2632826330
if (awaitedType === operandType && awaitedType !== errorType && !(operandType.flags & TypeFlags.AnyOrUnknown)) {
2632926331
addErrorOrSuggestion(/*isError*/ false, createDiagnosticForNode(node, Diagnostics.await_has_no_effect_on_the_type_of_this_expression));
2633026332
}
@@ -28830,9 +28832,9 @@ namespace ts {
2883028832
}
2883128833
}
2883228834

28833-
function getAwaitedTypeOfPromise(type: Type, errorNode?: Node, diagnosticMessage?: DiagnosticMessage, arg0?: string | number): Type | undefined {
28835+
function getAwaitedTypeOfPromise(type: Type, errorNode?: Node): Type | undefined {
2883428836
const promisedType = getPromisedTypeOfPromise(type, errorNode);
28835-
return promisedType && getAwaitedType(promisedType, errorNode, diagnosticMessage, arg0);
28837+
return promisedType && getAwaitedType(promisedType);
2883628838
}
2883728839

2883828840
/**
@@ -28900,12 +28902,12 @@ namespace ts {
2890028902
* Promise-like type; otherwise, it is the type of the expression. This is used to reflect
2890128903
* The runtime behavior of the `await` keyword.
2890228904
*/
28903-
function checkAwaitedType(type: Type, errorNode: Node, diagnosticMessage: DiagnosticMessage, arg0?: string | number): Type {
28904-
const awaitedType = getAwaitedType(type, errorNode, diagnosticMessage, arg0);
28905+
function checkAwaitedType(type: Type): Type {
28906+
const awaitedType = getAwaitedType(type);
2890528907
return awaitedType || errorType;
2890628908
}
2890728909

28908-
function getAwaitedType(type: Type, errorNode?: Node, diagnosticMessage?: DiagnosticMessage, arg0?: string | number): Type | undefined {
28910+
function getAwaitedType(type: Type): Type | undefined {
2890928911
const typeAsAwaitable = <PromiseOrAwaitableType>type;
2891028912
if (typeAsAwaitable.awaitedTypeOfType) {
2891128913
return typeAsAwaitable.awaitedTypeOfType;
@@ -28915,95 +28917,10 @@ namespace ts {
2891528917
return typeAsAwaitable.awaitedTypeOfType = type;
2891628918
}
2891728919

28918-
if (type.flags & TypeFlags.Union) {
28919-
let types: Type[] | undefined;
28920-
for (const constituentType of (<UnionType>type).types) {
28921-
types = append<Type>(types, getAwaitedType(constituentType, errorNode, diagnosticMessage, arg0));
28922-
}
28923-
28924-
if (!types) {
28925-
return undefined;
28926-
}
28927-
28928-
return typeAsAwaitable.awaitedTypeOfType = getUnionType(types);
28929-
}
28930-
28931-
const promisedType = getPromisedTypeOfPromise(type);
28932-
if (promisedType) {
28933-
if (type.id === promisedType.id || awaitedTypeStack.indexOf(promisedType.id) >= 0) {
28934-
// Verify that we don't have a bad actor in the form of a promise whose
28935-
// promised type is the same as the promise type, or a mutually recursive
28936-
// promise. If so, we return undefined as we cannot guess the shape. If this
28937-
// were the actual case in the JavaScript, this Promise would never resolve.
28938-
//
28939-
// An example of a bad actor with a singly-recursive promise type might
28940-
// be:
28941-
//
28942-
// interface BadPromise {
28943-
// then(
28944-
// onfulfilled: (value: BadPromise) => any,
28945-
// onrejected: (error: any) => any): BadPromise;
28946-
// }
28947-
// The above interface will pass the PromiseLike check, and return a
28948-
// promised type of `BadPromise`. Since this is a self reference, we
28949-
// don't want to keep recursing ad infinitum.
28950-
//
28951-
// An example of a bad actor in the form of a mutually-recursive
28952-
// promise type might be:
28953-
//
28954-
// interface BadPromiseA {
28955-
// then(
28956-
// onfulfilled: (value: BadPromiseB) => any,
28957-
// onrejected: (error: any) => any): BadPromiseB;
28958-
// }
28959-
//
28960-
// interface BadPromiseB {
28961-
// then(
28962-
// onfulfilled: (value: BadPromiseA) => any,
28963-
// onrejected: (error: any) => any): BadPromiseA;
28964-
// }
28965-
//
28966-
if (errorNode) {
28967-
error(errorNode, Diagnostics.Type_is_referenced_directly_or_indirectly_in_the_fulfillment_callback_of_its_own_then_method);
28968-
}
28969-
return undefined;
28970-
}
28971-
28972-
// Keep track of the type we're about to unwrap to avoid bad recursive promise types.
28973-
// See the comments above for more information.
28974-
awaitedTypeStack.push(type.id);
28975-
const awaitedType = getAwaitedType(promisedType, errorNode, diagnosticMessage, arg0);
28976-
awaitedTypeStack.pop();
28977-
28978-
if (!awaitedType) {
28979-
return undefined;
28980-
}
28981-
28982-
return typeAsAwaitable.awaitedTypeOfType = awaitedType;
28983-
}
28984-
28985-
// The type was not a promise, so it could not be unwrapped any further.
28986-
// As long as the type does not have a callable "then" property, it is
28987-
// safe to return the type; otherwise, an error will be reported in
28988-
// the call to getNonThenableType and we will return undefined.
28989-
//
28990-
// An example of a non-promise "thenable" might be:
28991-
//
28992-
// await { then(): void {} }
28993-
//
28994-
// The "thenable" does not match the minimal definition for a promise. When
28995-
// a Promise/A+-compatible or ES6 promise tries to adopt this value, the promise
28996-
// will never settle. We treat this as an error to help flag an early indicator
28997-
// of a runtime problem. If the user wants to return this value from an async
28998-
// function, they would need to wrap it in some other value. If they want it to
28999-
// be treated as a promise, they can cast to <any>.
29000-
const thenFunction = getTypeOfPropertyOfType(type, "then" as __String);
29001-
if (thenFunction && getSignaturesOfType(thenFunction, SignatureKind.Call).length > 0) {
29002-
if (errorNode) {
29003-
if (!diagnosticMessage) return Debug.fail();
29004-
error(errorNode, diagnosticMessage, arg0);
29005-
}
29006-
return undefined;
28920+
const symbol = getGlobalAwaitedSymbol(/*reportErrors*/ false);
28921+
if (symbol) {
28922+
const result = getTypeAliasInstantiation(symbol, [type]) as PromiseOrAwaitableType;
28923+
return typeAsAwaitable.awaitedTypeOfType = result.awaitedTypeOfType = result;
2900728924
}
2900828925

2900928926
return typeAsAwaitable.awaitedTypeOfType = type;
@@ -29109,7 +29026,7 @@ namespace ts {
2910929026
return;
2911029027
}
2911129028
}
29112-
checkAwaitedType(returnType, node, Diagnostics.The_return_type_of_an_async_function_must_either_be_a_valid_promise_or_must_not_contain_a_callable_then_member);
29029+
checkAwaitedType(returnType);
2911329030
}
2911429031

2911529032
/** Check a decorator */
@@ -30699,13 +30616,13 @@ namespace ts {
3069930616
return iterationTypes === noIterationTypes ? undefined : iterationTypes;
3070030617
}
3070130618

30702-
function getAsyncFromSyncIterationTypes(iterationTypes: IterationTypes, errorNode: Node | undefined) {
30619+
function getAsyncFromSyncIterationTypes(iterationTypes: IterationTypes) {
3070330620
if (iterationTypes === noIterationTypes) return noIterationTypes;
3070430621
if (iterationTypes === anyIterationTypes) return anyIterationTypes;
3070530622
const { yieldType, returnType, nextType } = iterationTypes;
3070630623
return createIterationTypes(
30707-
getAwaitedType(yieldType, errorNode) || anyType,
30708-
getAwaitedType(returnType, errorNode) || anyType,
30624+
getAwaitedType(yieldType) || anyType,
30625+
getAwaitedType(returnType) || anyType,
3070930626
nextType);
3071030627
}
3071130628

@@ -30741,7 +30658,7 @@ namespace ts {
3074130658
if (use & IterationUse.AllowsAsyncIterablesFlag) {
3074230659
// for a sync iterable in an async context, only use the cached types if they are valid.
3074330660
if (iterationTypes !== noIterationTypes) {
30744-
return (type as IterableOrIteratorType).iterationTypesOfAsyncIterable = getAsyncFromSyncIterationTypes(iterationTypes, errorNode);
30661+
return (type as IterableOrIteratorType).iterationTypesOfAsyncIterable = getAsyncFromSyncIterationTypes(iterationTypes);
3074530662
}
3074630663
}
3074730664
else {
@@ -30762,7 +30679,7 @@ namespace ts {
3076230679
if (iterationTypes !== noIterationTypes) {
3076330680
if (use & IterationUse.AllowsAsyncIterablesFlag) {
3076430681
return (type as IterableOrIteratorType).iterationTypesOfAsyncIterable = iterationTypes
30765-
? getAsyncFromSyncIterationTypes(iterationTypes, errorNode)
30682+
? getAsyncFromSyncIterationTypes(iterationTypes)
3076630683
: noIterationTypes;
3076730684
}
3076830685
else {
@@ -31170,7 +31087,7 @@ namespace ts {
3117031087
else if (getReturnTypeFromAnnotation(func)) {
3117131088
const unwrappedReturnType = unwrapReturnType(returnType, functionFlags);
3117231089
const unwrappedExprType = functionFlags & FunctionFlags.Async
31173-
? checkAwaitedType(exprType, node, Diagnostics.The_return_type_of_an_async_function_must_either_be_a_valid_promise_or_must_not_contain_a_callable_then_member)
31090+
? checkAwaitedType(exprType)
3117431091
: exprType;
3117531092
if (unwrappedReturnType) {
3117631093
// If the function has a return type, but promisedType is

src/harness/fourslash.ts

+1
Original file line numberDiff line numberDiff line change
@@ -4694,6 +4694,7 @@ namespace FourSlashInterface {
46944694
typeEntry("PropertyDecorator"),
46954695
typeEntry("MethodDecorator"),
46964696
typeEntry("ParameterDecorator"),
4697+
typeEntry("Awaited"),
46974698
typeEntry("PromiseConstructorLike"),
46984699
interfaceEntry("PromiseLike"),
46994700
interfaceEntry("Promise"),

src/lib/es2015.iterable.d.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -203,7 +203,7 @@ interface PromiseConstructor {
203203
* @param values An array 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
declare namespace Reflect {

0 commit comments

Comments
 (0)