Skip to content

Commit 12bfb7e

Browse files
authored
Merge pull request #9204 from Microsoft/promiseAndAsyncUpdates
Update Promise and PromiseLike, fix async functions issues with never.
2 parents 851a75e + 90e344e commit 12bfb7e

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
@@ -2794,6 +2794,10 @@ namespace ts {
27942794
return type && (type.flags & TypeFlags.Any) !== 0;
27952795
}
27962796

2797+
function isTypeNever(type: Type) {
2798+
return type && (type.flags & TypeFlags.Never) !== 0;
2799+
}
2800+
27972801
// Return the type of a binding element parent. We check SymbolLinks first to see if a type has been
27982802
// assigned by contextual typing.
27992803
function getTypeForBindingElementParent(node: VariableLikeDeclaration) {
@@ -11779,6 +11783,16 @@ namespace ts {
1177911783
return emptyObjectType;
1178011784
}
1178111785

11786+
function createPromiseReturnType(func: FunctionLikeDeclaration, promisedType: Type) {
11787+
const promiseType = createPromiseType(promisedType);
11788+
if (promiseType === emptyObjectType) {
11789+
error(func, Diagnostics.An_async_function_or_method_must_have_a_valid_awaitable_return_type);
11790+
return unknownType;
11791+
}
11792+
11793+
return promiseType;
11794+
}
11795+
1178211796
function getReturnTypeFromBody(func: FunctionLikeDeclaration, contextualMapper?: TypeMapper): Type {
1178311797
const contextualSignature = getContextualSignatureForFunctionLikeDeclaration(func);
1178411798
if (!func.body) {
@@ -11814,19 +11828,12 @@ namespace ts {
1181411828
else {
1181511829
types = checkAndAggregateReturnExpressionTypes(func, contextualMapper);
1181611830
if (!types) {
11817-
return neverType;
11831+
// For an async function, the return type will not be never, but rather a Promise for never.
11832+
return isAsync ? createPromiseReturnType(func, neverType) : neverType;
1181811833
}
1181911834
if (types.length === 0) {
11820-
if (isAsync) {
11821-
// For an async function, the return type will not be void, but rather a Promise for void.
11822-
const promiseType = createPromiseType(voidType);
11823-
if (promiseType === emptyObjectType) {
11824-
error(func, Diagnostics.An_async_function_or_method_must_have_a_valid_awaitable_return_type);
11825-
return unknownType;
11826-
}
11827-
return promiseType;
11828-
}
11829-
return voidType;
11835+
// For an async function, the return type will not be void, but rather a Promise for void.
11836+
return isAsync ? createPromiseReturnType(func, voidType) : voidType;
1183011837
}
1183111838
}
1183211839
// When yield/return statements are contextually typed we allow the return type to be a union type.
@@ -11840,7 +11847,7 @@ namespace ts {
1184011847
else {
1184111848
error(func, Diagnostics.No_best_common_type_exists_among_return_expressions);
1184211849
// Defer to unioning the return types so we get a) downstream errors earlier and b) better Salsa experience
11843-
return getUnionType(types);
11850+
return isAsync ? createPromiseReturnType(func, getUnionType(types)) : getUnionType(types);
1184411851
}
1184511852
}
1184611853

@@ -11853,21 +11860,10 @@ namespace ts {
1185311860
}
1185411861

1185511862
const widenedType = getWidenedType(type);
11856-
if (isAsync) {
11857-
// From within an async function you can return either a non-promise value or a promise. Any
11858-
// Promise/A+ compatible implementation will always assimilate any foreign promise, so the
11859-
// return type of the body is awaited type of the body, wrapped in a native Promise<T> type.
11860-
const promiseType = createPromiseType(widenedType);
11861-
if (promiseType === emptyObjectType) {
11862-
error(func, Diagnostics.An_async_function_or_method_must_have_a_valid_awaitable_return_type);
11863-
return unknownType;
11864-
}
11865-
11866-
return promiseType;
11867-
}
11868-
else {
11869-
return widenedType;
11870-
}
11863+
// From within an async function you can return either a non-promise value or a promise. Any
11864+
// Promise/A+ compatible implementation will always assimilate any foreign promise, so the
11865+
// return type of the body is awaited type of the body, wrapped in a native Promise<T> type.
11866+
return isAsync ? createPromiseReturnType(func, widenedType) : widenedType;
1187111867
}
1187211868

1187311869
function checkAndAggregateYieldOperandTypes(func: FunctionLikeDeclaration, contextualMapper: TypeMapper): Type[] {
@@ -13896,7 +13892,7 @@ namespace ts {
1389613892

1389713893
function checkNonThenableType(type: Type, location?: Node, message?: DiagnosticMessage) {
1389813894
type = getWidenedType(type);
13899-
if (!isTypeAny(type) && isTypeAssignableTo(type, getGlobalThenableType())) {
13895+
if (!isTypeAny(type) && !isTypeNever(type) && isTypeAssignableTo(type, getGlobalThenableType())) {
1390013896
if (location) {
1390113897
if (!message) {
1390213898
message = Diagnostics.Operand_for_await_does_not_have_a_valid_callable_then_member;
@@ -13927,12 +13923,15 @@ namespace ts {
1392713923
// }
1392813924
//
1392913925

13930-
if (promise.flags & TypeFlags.Any) {
13926+
if (isTypeAny(promise)) {
1393113927
return undefined;
1393213928
}
1393313929

13934-
if ((promise.flags & TypeFlags.Reference) && (<GenericType>promise).target === tryGetGlobalPromiseType()) {
13935-
return (<GenericType>promise).typeArguments[0];
13930+
if (promise.flags & TypeFlags.Reference) {
13931+
if ((<GenericType>promise).target === tryGetGlobalPromiseType()
13932+
|| (<GenericType>promise).target === getGlobalPromiseLikeType()) {
13933+
return (<GenericType>promise).typeArguments[0];
13934+
}
1393613935
}
1393713936

1393813937
const globalPromiseLikeType = getInstantiatedGlobalPromiseLikeType();
@@ -13941,17 +13940,17 @@ namespace ts {
1394113940
}
1394213941

1394313942
const thenFunction = getTypeOfPropertyOfType(promise, "then");
13944-
if (thenFunction && (thenFunction.flags & TypeFlags.Any)) {
13943+
if (!thenFunction || isTypeAny(thenFunction)) {
1394513944
return undefined;
1394613945
}
1394713946

13948-
const thenSignatures = thenFunction ? getSignaturesOfType(thenFunction, SignatureKind.Call) : emptyArray;
13947+
const thenSignatures = getSignaturesOfType(thenFunction, SignatureKind.Call);
1394913948
if (thenSignatures.length === 0) {
1395013949
return undefined;
1395113950
}
1395213951

1395313952
const onfulfilledParameterType = getTypeWithFacts(getUnionType(map(thenSignatures, getTypeOfFirstParameterOfSignature)), TypeFacts.NEUndefined);
13954-
if (onfulfilledParameterType.flags & TypeFlags.Any) {
13953+
if (isTypeAny(onfulfilledParameterType)) {
1395513954
return undefined;
1395613955
}
1395713956

@@ -13960,12 +13959,11 @@ namespace ts {
1396013959
return undefined;
1396113960
}
1396213961

13963-
const valueParameterType = getUnionType(map(onfulfilledParameterSignatures, getTypeOfFirstParameterOfSignature));
13964-
return valueParameterType;
13962+
return getUnionType(map(onfulfilledParameterSignatures, getTypeOfFirstParameterOfSignature));
1396513963
}
1396613964

1396713965
function getTypeOfFirstParameterOfSignature(signature: Signature) {
13968-
return getTypeAtPosition(signature, 0);
13966+
return signature.parameters.length > 0 ? getTypeAtPosition(signature, 0) : neverType;
1396913967
}
1397013968

1397113969
/**

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)