Skip to content

Commit 80dba4d

Browse files
committed
Support promise-like types in contextual return type of async function
1 parent 219bb44 commit 80dba4d

File tree

4 files changed

+99
-3
lines changed

4 files changed

+99
-3
lines changed

src/compiler/checker.ts

Lines changed: 24 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -495,6 +495,7 @@ namespace ts {
495495
let deferredGlobalESSymbolType: ObjectType;
496496
let deferredGlobalTypedPropertyDescriptorType: GenericType;
497497
let deferredGlobalPromiseType: GenericType;
498+
let deferredGlobalPromiseLikeType: GenericType;
498499
let deferredGlobalPromiseConstructorSymbol: Symbol | undefined;
499500
let deferredGlobalPromiseConstructorLikeType: ObjectType;
500501
let deferredGlobalIterableType: GenericType;
@@ -8538,6 +8539,10 @@ namespace ts {
85388539
return deferredGlobalPromiseType || (deferredGlobalPromiseType = getGlobalType("Promise" as __String, /*arity*/ 1, reportErrors)) || emptyGenericType;
85398540
}
85408541

8542+
function getGlobalPromiseLikeType(reportErrors: boolean) {
8543+
return deferredGlobalPromiseLikeType || (deferredGlobalPromiseLikeType = getGlobalType("PromiseLike" as __String, /*arity*/ 1, reportErrors)) || emptyGenericType;
8544+
}
8545+
85418546
function getGlobalPromiseConstructorSymbol(reportErrors: boolean): Symbol | undefined {
85428547
return deferredGlobalPromiseConstructorSymbol || (deferredGlobalPromiseConstructorSymbol = getGlobalValueSymbol("Promise" as __String, reportErrors));
85438548
}
@@ -16393,9 +16398,13 @@ namespace ts {
1639316398
}
1639416399

1639516400
const contextualReturnType = getContextualReturnType(func);
16396-
return functionFlags & FunctionFlags.Async
16397-
? contextualReturnType && getAwaitedTypeOfPromise(contextualReturnType) // Async function
16398-
: contextualReturnType; // Regular function
16401+
if (contextualReturnType) {
16402+
if (functionFlags & FunctionFlags.Async) { // Async function
16403+
const contextualAwaitedType = getAwaitedTypeOfPromise(contextualReturnType);
16404+
return contextualAwaitedType && getUnionType([contextualAwaitedType, createPromiseLikeType(contextualAwaitedType)]);
16405+
}
16406+
return contextualReturnType; // Regular function
16407+
}
1639916408
}
1640016409
return undefined;
1640116410
}
@@ -20796,6 +20805,18 @@ namespace ts {
2079620805
return emptyObjectType;
2079720806
}
2079820807

20808+
function createPromiseLikeType(promisedType: Type): Type {
20809+
// creates a `PromiseLike<T>` type where `T` is the promisedType argument
20810+
const globalPromiseLikeType = getGlobalPromiseLikeType(/*reportErrors*/ true);
20811+
if (globalPromiseLikeType !== emptyGenericType) {
20812+
// if the promised type is itself a promise, get the underlying type; otherwise, fallback to the promised type
20813+
promisedType = getAwaitedType(promisedType) || emptyObjectType;
20814+
return createTypeReference(globalPromiseLikeType, [promisedType]);
20815+
}
20816+
20817+
return emptyObjectType;
20818+
}
20819+
2079920820
function createPromiseReturnType(func: FunctionLikeDeclaration | ImportCall, promisedType: Type) {
2080020821
const promiseType = createPromiseType(promisedType);
2080120822
if (promiseType === emptyObjectType) {
Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
=== tests/cases/conformance/types/contextualTypes/asyncFunctions/contextuallyTypeAsyncFunctionReturnType.ts ===
2+
interface Obj { key: "value"; }
3+
>Obj : Symbol(Obj, Decl(contextuallyTypeAsyncFunctionReturnType.ts, 0, 0))
4+
>key : Symbol(Obj.key, Decl(contextuallyTypeAsyncFunctionReturnType.ts, 0, 15))
5+
6+
async function fn1(): Promise<Obj> {
7+
>fn1 : Symbol(fn1, Decl(contextuallyTypeAsyncFunctionReturnType.ts, 0, 31))
8+
>Promise : Symbol(Promise, Decl(lib.es5.d.ts, --, --), Decl(lib.es2015.iterable.d.ts, --, --), Decl(lib.es2015.promise.d.ts, --, --), Decl(lib.es2015.symbol.wellknown.d.ts, --, --), Decl(lib.es2018.promise.d.ts, --, --))
9+
>Obj : Symbol(Obj, Decl(contextuallyTypeAsyncFunctionReturnType.ts, 0, 0))
10+
11+
return { key: "value" };
12+
>key : Symbol(key, Decl(contextuallyTypeAsyncFunctionReturnType.ts, 3, 12))
13+
}
14+
15+
async function fn2(): Promise<Obj> {
16+
>fn2 : Symbol(fn2, Decl(contextuallyTypeAsyncFunctionReturnType.ts, 4, 1))
17+
>Promise : Symbol(Promise, Decl(lib.es5.d.ts, --, --), Decl(lib.es2015.iterable.d.ts, --, --), Decl(lib.es2015.promise.d.ts, --, --), Decl(lib.es2015.symbol.wellknown.d.ts, --, --), Decl(lib.es2018.promise.d.ts, --, --))
18+
>Obj : Symbol(Obj, Decl(contextuallyTypeAsyncFunctionReturnType.ts, 0, 0))
19+
20+
return new Promise(resolve => {
21+
>Promise : Symbol(Promise, Decl(lib.es5.d.ts, --, --), Decl(lib.es2015.iterable.d.ts, --, --), Decl(lib.es2015.promise.d.ts, --, --), Decl(lib.es2015.symbol.wellknown.d.ts, --, --), Decl(lib.es2018.promise.d.ts, --, --))
22+
>resolve : Symbol(resolve, Decl(contextuallyTypeAsyncFunctionReturnType.ts, 7, 23))
23+
24+
resolve({ key: "value" });
25+
>resolve : Symbol(resolve, Decl(contextuallyTypeAsyncFunctionReturnType.ts, 7, 23))
26+
>key : Symbol(key, Decl(contextuallyTypeAsyncFunctionReturnType.ts, 8, 17))
27+
28+
});
29+
}
Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
=== tests/cases/conformance/types/contextualTypes/asyncFunctions/contextuallyTypeAsyncFunctionReturnType.ts ===
2+
interface Obj { key: "value"; }
3+
>key : "value"
4+
5+
async function fn1(): Promise<Obj> {
6+
>fn1 : () => Promise<Obj>
7+
8+
return { key: "value" };
9+
>{ key: "value" } : { key: "value"; }
10+
>key : "value"
11+
>"value" : "value"
12+
}
13+
14+
async function fn2(): Promise<Obj> {
15+
>fn2 : () => Promise<Obj>
16+
17+
return new Promise(resolve => {
18+
>new Promise(resolve => { resolve({ key: "value" }); }) : Promise<Obj>
19+
>Promise : PromiseConstructor
20+
>resolve => { resolve({ key: "value" }); } : (resolve: (value?: Obj | PromiseLike<Obj>) => void) => void
21+
>resolve : (value?: Obj | PromiseLike<Obj>) => void
22+
23+
resolve({ key: "value" });
24+
>resolve({ key: "value" }) : void
25+
>resolve : (value?: Obj | PromiseLike<Obj>) => void
26+
>{ key: "value" } : { key: "value"; }
27+
>key : "value"
28+
>"value" : "value"
29+
30+
});
31+
}
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
// @target: esnext
2+
// @noImplicitAny: true
3+
// @noEmit: true
4+
5+
interface Obj { key: "value"; }
6+
7+
async function fn1(): Promise<Obj> {
8+
return { key: "value" };
9+
}
10+
11+
async function fn2(): Promise<Obj> {
12+
return new Promise(resolve => {
13+
resolve({ key: "value" });
14+
});
15+
}

0 commit comments

Comments
 (0)