Skip to content

Commit fb9806b

Browse files
committed
Updates type definitions for Promise and PromiseLike, fixes issues in async functions due to introduction of never type.
Fixes #9193.
1 parent 7bb739f commit fb9806b

17 files changed

+1024
-100
lines changed

src/compiler/checker.ts

Lines changed: 35 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -2768,6 +2768,10 @@ namespace ts {
27682768
return type && (type.flags & TypeFlags.Any) !== 0;
27692769
}
27702770

2771+
function isTypeNever(type: Type) {
2772+
return type && (type.flags & TypeFlags.Never) !== 0;
2773+
}
2774+
27712775
// Return the type of a binding element parent. We check SymbolLinks first to see if a type has been
27722776
// assigned by contextual typing.
27732777
function getTypeForBindingElementParent(node: VariableLikeDeclaration) {
@@ -11655,6 +11659,16 @@ namespace ts {
1165511659
return emptyObjectType;
1165611660
}
1165711661

11662+
function createPromiseReturnType(func: FunctionLikeDeclaration, promisedType: Type) {
11663+
const promiseType = createPromiseType(promisedType);
11664+
if (promiseType === emptyObjectType) {
11665+
error(func, Diagnostics.An_async_function_or_method_must_have_a_valid_awaitable_return_type);
11666+
return unknownType;
11667+
}
11668+
11669+
return promiseType;
11670+
}
11671+
1165811672
function getReturnTypeFromBody(func: FunctionLikeDeclaration, contextualMapper?: TypeMapper): Type {
1165911673
const contextualSignature = getContextualSignatureForFunctionLikeDeclaration(func);
1166011674
if (!func.body) {
@@ -11690,19 +11704,12 @@ namespace ts {
1169011704
else {
1169111705
types = checkAndAggregateReturnExpressionTypes(func, contextualMapper);
1169211706
if (!types) {
11693-
return neverType;
11707+
// For an async function, the return type will not be never, but rather a Promise for never.
11708+
return isAsync ? createPromiseReturnType(func, neverType) : neverType;
1169411709
}
1169511710
if (types.length === 0) {
11696-
if (isAsync) {
11697-
// For an async function, the return type will not be void, but rather a Promise for void.
11698-
const promiseType = createPromiseType(voidType);
11699-
if (promiseType === emptyObjectType) {
11700-
error(func, Diagnostics.An_async_function_or_method_must_have_a_valid_awaitable_return_type);
11701-
return unknownType;
11702-
}
11703-
return promiseType;
11704-
}
11705-
return voidType;
11711+
// For an async function, the return type will not be void, but rather a Promise for void.
11712+
return isAsync ? createPromiseReturnType(func, voidType): voidType;
1170611713
}
1170711714
}
1170811715
// When yield/return statements are contextually typed we allow the return type to be a union type.
@@ -11716,7 +11723,7 @@ namespace ts {
1171611723
else {
1171711724
error(func, Diagnostics.No_best_common_type_exists_among_return_expressions);
1171811725
// Defer to unioning the return types so we get a) downstream errors earlier and b) better Salsa experience
11719-
return getUnionType(types);
11726+
return isAsync ? createPromiseReturnType(func, getUnionType(types)) : getUnionType(types);
1172011727
}
1172111728
}
1172211729

@@ -11729,21 +11736,10 @@ namespace ts {
1172911736
}
1173011737

1173111738
const widenedType = getWidenedType(type);
11732-
if (isAsync) {
11733-
// From within an async function you can return either a non-promise value or a promise. Any
11734-
// Promise/A+ compatible implementation will always assimilate any foreign promise, so the
11735-
// return type of the body is awaited type of the body, wrapped in a native Promise<T> type.
11736-
const promiseType = createPromiseType(widenedType);
11737-
if (promiseType === emptyObjectType) {
11738-
error(func, Diagnostics.An_async_function_or_method_must_have_a_valid_awaitable_return_type);
11739-
return unknownType;
11740-
}
11741-
11742-
return promiseType;
11743-
}
11744-
else {
11745-
return widenedType;
11746-
}
11739+
// From within an async function you can return either a non-promise value or a promise. Any
11740+
// Promise/A+ compatible implementation will always assimilate any foreign promise, so the
11741+
// return type of the body is awaited type of the body, wrapped in a native Promise<T> type.
11742+
return isAsync ? createPromiseReturnType(func, widenedType) : widenedType;
1174711743
}
1174811744

1174911745
function checkAndAggregateYieldOperandTypes(func: FunctionLikeDeclaration, contextualMapper: TypeMapper): Type[] {
@@ -13740,7 +13736,7 @@ namespace ts {
1374013736

1374113737
function checkNonThenableType(type: Type, location?: Node, message?: DiagnosticMessage) {
1374213738
type = getWidenedType(type);
13743-
if (!isTypeAny(type) && isTypeAssignableTo(type, getGlobalThenableType())) {
13739+
if (!isTypeAny(type) && !isTypeNever(type) && isTypeAssignableTo(type, getGlobalThenableType())) {
1374413740
if (location) {
1374513741
if (!message) {
1374613742
message = Diagnostics.Operand_for_await_does_not_have_a_valid_callable_then_member;
@@ -13771,12 +13767,15 @@ namespace ts {
1377113767
// }
1377213768
//
1377313769

13774-
if (promise.flags & TypeFlags.Any) {
13770+
if (isTypeAny(promise)) {
1377513771
return undefined;
1377613772
}
1377713773

13778-
if ((promise.flags & TypeFlags.Reference) && (<GenericType>promise).target === tryGetGlobalPromiseType()) {
13779-
return (<GenericType>promise).typeArguments[0];
13774+
if (promise.flags & TypeFlags.Reference) {
13775+
if ((<GenericType>promise).target === tryGetGlobalPromiseType()
13776+
|| (<GenericType>promise).target === getGlobalPromiseLikeType()) {
13777+
return (<GenericType>promise).typeArguments[0];
13778+
}
1378013779
}
1378113780

1378213781
const globalPromiseLikeType = getInstantiatedGlobalPromiseLikeType();
@@ -13785,17 +13784,17 @@ namespace ts {
1378513784
}
1378613785

1378713786
const thenFunction = getTypeOfPropertyOfType(promise, "then");
13788-
if (thenFunction && (thenFunction.flags & TypeFlags.Any)) {
13787+
if (!thenFunction || isTypeAny(thenFunction)) {
1378913788
return undefined;
1379013789
}
1379113790

13792-
const thenSignatures = thenFunction ? getSignaturesOfType(thenFunction, SignatureKind.Call) : emptyArray;
13791+
const thenSignatures = getSignaturesOfType(thenFunction, SignatureKind.Call);
1379313792
if (thenSignatures.length === 0) {
1379413793
return undefined;
1379513794
}
1379613795

1379713796
const onfulfilledParameterType = getTypeWithFacts(getUnionType(map(thenSignatures, getTypeOfFirstParameterOfSignature)), TypeFacts.NEUndefined);
13798-
if (onfulfilledParameterType.flags & TypeFlags.Any) {
13797+
if (isTypeAny(onfulfilledParameterType)) {
1379913798
return undefined;
1380013799
}
1380113800

@@ -13804,12 +13803,11 @@ namespace ts {
1380413803
return undefined;
1380513804
}
1380613805

13807-
const valueParameterType = getUnionType(map(onfulfilledParameterSignatures, getTypeOfFirstParameterOfSignature));
13808-
return valueParameterType;
13806+
return getUnionType(map(onfulfilledParameterSignatures, getTypeOfFirstParameterOfSignature));
1380913807
}
1381013808

1381113809
function getTypeOfFirstParameterOfSignature(signature: Signature) {
13812-
return getTypeAtPosition(signature, 0);
13810+
return signature.parameters.length > 0 ? getTypeAtPosition(signature, 0) : neverType;
1381313811
}
1381413812

1381513813
/**

src/lib/es2015.promise.d.ts

Lines changed: 105 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -3,59 +3,149 @@
33
*/
44
interface Promise<T> {
55
/**
6-
* Attaches callbacks for the resolution and/or rejection of the Promise.
7-
* @param onfulfilled The callback to execute when the Promise is resolved.
8-
* @param onrejected The callback to execute when the Promise is rejected.
9-
* @returns A Promise for the completion of which ever callback is executed.
10-
*/
11-
then<TResult>(onfulfilled?: (value: T) => TResult | PromiseLike<TResult>, onrejected?: (reason: any) => TResult | PromiseLike<TResult>): Promise<TResult>;
12-
then<TResult>(onfulfilled?: (value: T) => TResult | PromiseLike<TResult>, onrejected?: (reason: any) => void): Promise<TResult>;
6+
* Attaches callbacks for the resolution and/or rejection of the Promise.
7+
* @param onfulfilled The callback to execute when the Promise is resolved.
8+
* @param onrejected The callback to execute when the Promise is rejected.
9+
* @returns A Promise for the completion of which ever callback is executed.
10+
*/
11+
then<TResult1, TResult2>(onfulfilled: (value: T) => TResult1 | PromiseLike<TResult1>, onrejected: (reason: any) => TResult2 | PromiseLike<TResult2>): Promise<TResult1 | TResult2>;
12+
13+
/**
14+
* Attaches callbacks for the resolution and/or rejection of the Promise.
15+
* @param onfulfilled The callback to execute when the Promise is resolved.
16+
* @param onrejected The callback to execute when the Promise is rejected.
17+
* @returns A Promise for the completion of which ever callback is executed.
18+
*/
19+
then<TResult>(onfulfilled: (value: T) => TResult | PromiseLike<TResult>, onrejected: (reason: any) => TResult | PromiseLike<TResult>): Promise<TResult>;
20+
21+
/**
22+
* Attaches callbacks for the resolution and/or rejection of the Promise.
23+
* @param onfulfilled The callback to execute when the Promise is resolved.
24+
* @returns A Promise for the completion of which ever callback is executed.
25+
*/
26+
then<TResult>(onfulfilled: (value: T) => TResult | PromiseLike<TResult>): Promise<TResult>;
27+
28+
/**
29+
* Creates a new Promise with the same internal state of this Promise.
30+
* @returns A Promise.
31+
*/
32+
then(): Promise<T>;
1333

1434
/**
1535
* Attaches a callback for only the rejection of the Promise.
1636
* @param onrejected The callback to execute when the Promise is rejected.
1737
* @returns A Promise for the completion of the callback.
1838
*/
19-
catch(onrejected?: (reason: any) => T | PromiseLike<T>): Promise<T>;
20-
catch(onrejected?: (reason: any) => void): Promise<T>;
39+
catch<TResult>(onrejected: (reason: any) => TResult | PromiseLike<TResult>): Promise<T | TResult>;
40+
41+
/**
42+
* Attaches a callback for only the rejection of the Promise.
43+
* @param onrejected The callback to execute when the Promise is rejected.
44+
* @returns A Promise for the completion of the callback.
45+
*/
46+
catch(onrejected: (reason: any) => T | PromiseLike<T>): Promise<T>;
2147
}
2248

2349
interface PromiseConstructor {
24-
/**
25-
* A reference to the prototype.
50+
/**
51+
* A reference to the prototype.
2652
*/
2753
readonly prototype: Promise<any>;
2854

2955
/**
3056
* Creates a new Promise.
31-
* @param executor A callback used to initialize the promise. This callback is passed two arguments:
32-
* a resolve callback used resolve the promise with a value or the result of another promise,
57+
* @param executor A callback used to initialize the promise. This callback is passed two arguments:
58+
* a resolve callback used resolve the promise with a value or the result of another promise,
3359
* and a reject callback used to reject the promise with a provided reason or error.
3460
*/
3561
new <T>(executor: (resolve: (value?: T | PromiseLike<T>) => void, reject: (reason?: any) => void) => void): Promise<T>;
3662

3763
/**
38-
* Creates a Promise that is resolved with an array of results when all of the provided Promises
64+
* Creates a Promise that is resolved with an array of results when all of the provided Promises
3965
* resolve, or rejected when any Promise is rejected.
4066
* @param values An array of Promises.
4167
* @returns A new Promise.
4268
*/
4369
all<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10>(values: [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]>;
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+
*/
4477
all<T1, T2, T3, T4, T5, T6, T7, T8, T9>(values: [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]>;
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+
*/
4585
all<T1, T2, T3, T4, T5, T6, T7, T8>(values: [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]>;
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+
*/
4693
all<T1, T2, T3, T4, T5, T6, T7>(values: [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]>;
94+
95+
/**
96+
* Creates a Promise that is resolved with an array of results when all of the provided Promises
97+
* resolve, or rejected when any Promise is rejected.
98+
* @param values An array of Promises.
99+
* @returns A new Promise.
100+
*/
47101
all<T1, T2, T3, T4, T5, T6>(values: [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]>;
102+
103+
/**
104+
* Creates a Promise that is resolved with an array of results when all of the provided Promises
105+
* resolve, or rejected when any Promise is rejected.
106+
* @param values An array of Promises.
107+
* @returns A new Promise.
108+
*/
48109
all<T1, T2, T3, T4, T5>(values: [T1 | PromiseLike<T1>, T2 | PromiseLike<T2>, T3 | PromiseLike<T3>, T4 | PromiseLike <T4>, T5 | PromiseLike<T5>]): Promise<[T1, T2, T3, T4, T5]>;
110+
111+
/**
112+
* Creates a Promise that is resolved with an array of results when all of the provided Promises
113+
* resolve, or rejected when any Promise is rejected.
114+
* @param values An array of Promises.
115+
* @returns A new Promise.
116+
*/
49117
all<T1, T2, T3, T4>(values: [T1 | PromiseLike<T1>, T2 | PromiseLike<T2>, T3 | PromiseLike<T3>, T4 | PromiseLike <T4>]): Promise<[T1, T2, T3, T4]>;
118+
119+
/**
120+
* Creates a Promise that is resolved with an array of results when all of the provided Promises
121+
* resolve, or rejected when any Promise is rejected.
122+
* @param values An array of Promises.
123+
* @returns A new Promise.
124+
*/
50125
all<T1, T2, T3>(values: [T1 | PromiseLike<T1>, T2 | PromiseLike<T2>, T3 | PromiseLike<T3>]): Promise<[T1, T2, T3]>;
126+
127+
/**
128+
* Creates a Promise that is resolved with an array of results when all of the provided Promises
129+
* resolve, or rejected when any Promise is rejected.
130+
* @param values An array of Promises.
131+
* @returns A new Promise.
132+
*/
51133
all<T1, T2>(values: [T1 | PromiseLike<T1>, T2 | PromiseLike<T2>]): Promise<[T1, T2]>;
52134

135+
/**
136+
* Creates a Promise that is resolved with an array of results when all of the provided Promises
137+
* resolve, or rejected when any Promise is rejected.
138+
* @param values An array of Promises.
139+
* @returns A new Promise.
140+
*/
141+
all<T>(values: (T | PromiseLike<T>)[]): Promise<T[]>;
142+
53143
/**
54144
* Creates a new rejected promise for the provided reason.
55145
* @param reason The reason the promise was rejected.
56146
* @returns A new rejected Promise.
57147
*/
58-
reject(reason: any): Promise<void>;
148+
reject(reason: any): Promise<never>;
59149

60150
/**
61151
* Creates a new rejected promise for the provided reason.

0 commit comments

Comments
 (0)