Skip to content

Commit 2f0cc51

Browse files
authored
Fix contextual types for maybe-async callbacks (#37205)
* Fix contextual types for maybe-async callbacks * Remove comment
1 parent 9cd4b07 commit 2f0cc51

5 files changed

+224
-1
lines changed

src/compiler/checker.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21810,7 +21810,7 @@ namespace ts {
2181021810
const contextualReturnType = getContextualReturnType(func);
2181121811
if (contextualReturnType) {
2181221812
if (functionFlags & FunctionFlags.Async) { // Async function
21813-
const contextualAwaitedType = getAwaitedTypeOfPromise(contextualReturnType);
21813+
const contextualAwaitedType = mapType(contextualReturnType, getAwaitedTypeOfPromise);
2181421814
return contextualAwaitedType && getUnionType([contextualAwaitedType, createPromiseLikeType(contextualAwaitedType)]);
2181521815
}
2181621816
return contextualReturnType; // Regular function
Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
//// [asyncFunctionContextuallyTypedReturns.ts]
2+
declare function f(cb: (v: boolean) => [0] | PromiseLike<[0]>): void;
3+
f(v => v ? [0] : Promise.reject());
4+
f(async v => v ? [0] : Promise.reject());
5+
6+
declare function g(cb: (v: boolean) => "contextuallyTypable" | PromiseLike<"contextuallyTypable">): void;
7+
g(v => v ? "contextuallyTypable" : Promise.reject());
8+
g(async v => v ? "contextuallyTypable" : Promise.reject());
9+
10+
type MyCallback = (thing: string) => void;
11+
declare function h(cb: (v: boolean) => MyCallback | PromiseLike<MyCallback>): void;
12+
h(v => v ? (abc) => { } : Promise.reject());
13+
h(async v => v ? (def) => { } : Promise.reject());
14+
15+
16+
//// [asyncFunctionContextuallyTypedReturns.js]
17+
"use strict";
18+
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
19+
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
20+
return new (P || (P = Promise))(function (resolve, reject) {
21+
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
22+
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
23+
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
24+
step((generator = generator.apply(thisArg, _arguments || [])).next());
25+
});
26+
};
27+
f(v => v ? [0] : Promise.reject());
28+
f((v) => __awaiter(void 0, void 0, void 0, function* () { return v ? [0] : Promise.reject(); }));
29+
g(v => v ? "contextuallyTypable" : Promise.reject());
30+
g((v) => __awaiter(void 0, void 0, void 0, function* () { return v ? "contextuallyTypable" : Promise.reject(); }));
31+
h(v => v ? (abc) => { } : Promise.reject());
32+
h((v) => __awaiter(void 0, void 0, void 0, function* () { return v ? (def) => { } : Promise.reject(); }));
Lines changed: 75 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,75 @@
1+
=== tests/cases/compiler/asyncFunctionContextuallyTypedReturns.ts ===
2+
declare function f(cb: (v: boolean) => [0] | PromiseLike<[0]>): void;
3+
>f : Symbol(f, Decl(asyncFunctionContextuallyTypedReturns.ts, 0, 0))
4+
>cb : Symbol(cb, Decl(asyncFunctionContextuallyTypedReturns.ts, 0, 19))
5+
>v : Symbol(v, Decl(asyncFunctionContextuallyTypedReturns.ts, 0, 24))
6+
>PromiseLike : Symbol(PromiseLike, Decl(lib.es5.d.ts, --, --))
7+
8+
f(v => v ? [0] : Promise.reject());
9+
>f : Symbol(f, Decl(asyncFunctionContextuallyTypedReturns.ts, 0, 0))
10+
>v : Symbol(v, Decl(asyncFunctionContextuallyTypedReturns.ts, 1, 2))
11+
>v : Symbol(v, Decl(asyncFunctionContextuallyTypedReturns.ts, 1, 2))
12+
>Promise.reject : Symbol(PromiseConstructor.reject, Decl(lib.es2015.promise.d.ts, --, --))
13+
>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, --, --))
14+
>reject : Symbol(PromiseConstructor.reject, Decl(lib.es2015.promise.d.ts, --, --))
15+
16+
f(async v => v ? [0] : Promise.reject());
17+
>f : Symbol(f, Decl(asyncFunctionContextuallyTypedReturns.ts, 0, 0))
18+
>v : Symbol(v, Decl(asyncFunctionContextuallyTypedReturns.ts, 2, 7))
19+
>v : Symbol(v, Decl(asyncFunctionContextuallyTypedReturns.ts, 2, 7))
20+
>Promise.reject : Symbol(PromiseConstructor.reject, Decl(lib.es2015.promise.d.ts, --, --))
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, --, --))
22+
>reject : Symbol(PromiseConstructor.reject, Decl(lib.es2015.promise.d.ts, --, --))
23+
24+
declare function g(cb: (v: boolean) => "contextuallyTypable" | PromiseLike<"contextuallyTypable">): void;
25+
>g : Symbol(g, Decl(asyncFunctionContextuallyTypedReturns.ts, 2, 41))
26+
>cb : Symbol(cb, Decl(asyncFunctionContextuallyTypedReturns.ts, 4, 19))
27+
>v : Symbol(v, Decl(asyncFunctionContextuallyTypedReturns.ts, 4, 24))
28+
>PromiseLike : Symbol(PromiseLike, Decl(lib.es5.d.ts, --, --))
29+
30+
g(v => v ? "contextuallyTypable" : Promise.reject());
31+
>g : Symbol(g, Decl(asyncFunctionContextuallyTypedReturns.ts, 2, 41))
32+
>v : Symbol(v, Decl(asyncFunctionContextuallyTypedReturns.ts, 5, 2))
33+
>v : Symbol(v, Decl(asyncFunctionContextuallyTypedReturns.ts, 5, 2))
34+
>Promise.reject : Symbol(PromiseConstructor.reject, Decl(lib.es2015.promise.d.ts, --, --))
35+
>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, --, --))
36+
>reject : Symbol(PromiseConstructor.reject, Decl(lib.es2015.promise.d.ts, --, --))
37+
38+
g(async v => v ? "contextuallyTypable" : Promise.reject());
39+
>g : Symbol(g, Decl(asyncFunctionContextuallyTypedReturns.ts, 2, 41))
40+
>v : Symbol(v, Decl(asyncFunctionContextuallyTypedReturns.ts, 6, 7))
41+
>v : Symbol(v, Decl(asyncFunctionContextuallyTypedReturns.ts, 6, 7))
42+
>Promise.reject : Symbol(PromiseConstructor.reject, Decl(lib.es2015.promise.d.ts, --, --))
43+
>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, --, --))
44+
>reject : Symbol(PromiseConstructor.reject, Decl(lib.es2015.promise.d.ts, --, --))
45+
46+
type MyCallback = (thing: string) => void;
47+
>MyCallback : Symbol(MyCallback, Decl(asyncFunctionContextuallyTypedReturns.ts, 6, 59))
48+
>thing : Symbol(thing, Decl(asyncFunctionContextuallyTypedReturns.ts, 8, 19))
49+
50+
declare function h(cb: (v: boolean) => MyCallback | PromiseLike<MyCallback>): void;
51+
>h : Symbol(h, Decl(asyncFunctionContextuallyTypedReturns.ts, 8, 42))
52+
>cb : Symbol(cb, Decl(asyncFunctionContextuallyTypedReturns.ts, 9, 19))
53+
>v : Symbol(v, Decl(asyncFunctionContextuallyTypedReturns.ts, 9, 24))
54+
>MyCallback : Symbol(MyCallback, Decl(asyncFunctionContextuallyTypedReturns.ts, 6, 59))
55+
>PromiseLike : Symbol(PromiseLike, Decl(lib.es5.d.ts, --, --))
56+
>MyCallback : Symbol(MyCallback, Decl(asyncFunctionContextuallyTypedReturns.ts, 6, 59))
57+
58+
h(v => v ? (abc) => { } : Promise.reject());
59+
>h : Symbol(h, Decl(asyncFunctionContextuallyTypedReturns.ts, 8, 42))
60+
>v : Symbol(v, Decl(asyncFunctionContextuallyTypedReturns.ts, 10, 2))
61+
>v : Symbol(v, Decl(asyncFunctionContextuallyTypedReturns.ts, 10, 2))
62+
>abc : Symbol(abc, Decl(asyncFunctionContextuallyTypedReturns.ts, 10, 12))
63+
>Promise.reject : Symbol(PromiseConstructor.reject, Decl(lib.es2015.promise.d.ts, --, --))
64+
>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, --, --))
65+
>reject : Symbol(PromiseConstructor.reject, Decl(lib.es2015.promise.d.ts, --, --))
66+
67+
h(async v => v ? (def) => { } : Promise.reject());
68+
>h : Symbol(h, Decl(asyncFunctionContextuallyTypedReturns.ts, 8, 42))
69+
>v : Symbol(v, Decl(asyncFunctionContextuallyTypedReturns.ts, 11, 7))
70+
>v : Symbol(v, Decl(asyncFunctionContextuallyTypedReturns.ts, 11, 7))
71+
>def : Symbol(def, Decl(asyncFunctionContextuallyTypedReturns.ts, 11, 18))
72+
>Promise.reject : Symbol(PromiseConstructor.reject, Decl(lib.es2015.promise.d.ts, --, --))
73+
>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, --, --))
74+
>reject : Symbol(PromiseConstructor.reject, Decl(lib.es2015.promise.d.ts, --, --))
75+
Lines changed: 102 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,102 @@
1+
=== tests/cases/compiler/asyncFunctionContextuallyTypedReturns.ts ===
2+
declare function f(cb: (v: boolean) => [0] | PromiseLike<[0]>): void;
3+
>f : (cb: (v: boolean) => [0] | PromiseLike<[0]>) => void
4+
>cb : (v: boolean) => [0] | PromiseLike<[0]>
5+
>v : boolean
6+
7+
f(v => v ? [0] : Promise.reject());
8+
>f(v => v ? [0] : Promise.reject()) : void
9+
>f : (cb: (v: boolean) => [0] | PromiseLike<[0]>) => void
10+
>v => v ? [0] : Promise.reject() : (v: boolean) => [0] | Promise<[0]>
11+
>v : boolean
12+
>v ? [0] : Promise.reject() : [0] | Promise<[0]>
13+
>v : boolean
14+
>[0] : [0]
15+
>0 : 0
16+
>Promise.reject() : Promise<[0]>
17+
>Promise.reject : <T = never>(reason?: any) => Promise<T>
18+
>Promise : PromiseConstructor
19+
>reject : <T = never>(reason?: any) => Promise<T>
20+
21+
f(async v => v ? [0] : Promise.reject());
22+
>f(async v => v ? [0] : Promise.reject()) : void
23+
>f : (cb: (v: boolean) => [0] | PromiseLike<[0]>) => void
24+
>async v => v ? [0] : Promise.reject() : (v: boolean) => Promise<[0] | [0]>
25+
>v : boolean
26+
>v ? [0] : Promise.reject() : [0] | Promise<[0]>
27+
>v : boolean
28+
>[0] : [0]
29+
>0 : 0
30+
>Promise.reject() : Promise<[0]>
31+
>Promise.reject : <T = never>(reason?: any) => Promise<T>
32+
>Promise : PromiseConstructor
33+
>reject : <T = never>(reason?: any) => Promise<T>
34+
35+
declare function g(cb: (v: boolean) => "contextuallyTypable" | PromiseLike<"contextuallyTypable">): void;
36+
>g : (cb: (v: boolean) => "contextuallyTypable" | PromiseLike<"contextuallyTypable">) => void
37+
>cb : (v: boolean) => "contextuallyTypable" | PromiseLike<"contextuallyTypable">
38+
>v : boolean
39+
40+
g(v => v ? "contextuallyTypable" : Promise.reject());
41+
>g(v => v ? "contextuallyTypable" : Promise.reject()) : void
42+
>g : (cb: (v: boolean) => "contextuallyTypable" | PromiseLike<"contextuallyTypable">) => void
43+
>v => v ? "contextuallyTypable" : Promise.reject() : (v: boolean) => "contextuallyTypable" | Promise<"contextuallyTypable">
44+
>v : boolean
45+
>v ? "contextuallyTypable" : Promise.reject() : "contextuallyTypable" | Promise<"contextuallyTypable">
46+
>v : boolean
47+
>"contextuallyTypable" : "contextuallyTypable"
48+
>Promise.reject() : Promise<"contextuallyTypable">
49+
>Promise.reject : <T = never>(reason?: any) => Promise<T>
50+
>Promise : PromiseConstructor
51+
>reject : <T = never>(reason?: any) => Promise<T>
52+
53+
g(async v => v ? "contextuallyTypable" : Promise.reject());
54+
>g(async v => v ? "contextuallyTypable" : Promise.reject()) : void
55+
>g : (cb: (v: boolean) => "contextuallyTypable" | PromiseLike<"contextuallyTypable">) => void
56+
>async v => v ? "contextuallyTypable" : Promise.reject() : (v: boolean) => Promise<"contextuallyTypable">
57+
>v : boolean
58+
>v ? "contextuallyTypable" : Promise.reject() : "contextuallyTypable" | Promise<"contextuallyTypable">
59+
>v : boolean
60+
>"contextuallyTypable" : "contextuallyTypable"
61+
>Promise.reject() : Promise<"contextuallyTypable">
62+
>Promise.reject : <T = never>(reason?: any) => Promise<T>
63+
>Promise : PromiseConstructor
64+
>reject : <T = never>(reason?: any) => Promise<T>
65+
66+
type MyCallback = (thing: string) => void;
67+
>MyCallback : MyCallback
68+
>thing : string
69+
70+
declare function h(cb: (v: boolean) => MyCallback | PromiseLike<MyCallback>): void;
71+
>h : (cb: (v: boolean) => MyCallback | PromiseLike<MyCallback>) => void
72+
>cb : (v: boolean) => MyCallback | PromiseLike<MyCallback>
73+
>v : boolean
74+
75+
h(v => v ? (abc) => { } : Promise.reject());
76+
>h(v => v ? (abc) => { } : Promise.reject()) : void
77+
>h : (cb: (v: boolean) => MyCallback | PromiseLike<MyCallback>) => void
78+
>v => v ? (abc) => { } : Promise.reject() : (v: boolean) => ((abc: string) => void) | Promise<MyCallback>
79+
>v : boolean
80+
>v ? (abc) => { } : Promise.reject() : ((abc: string) => void) | Promise<MyCallback>
81+
>v : boolean
82+
>(abc) => { } : (abc: string) => void
83+
>abc : string
84+
>Promise.reject() : Promise<MyCallback>
85+
>Promise.reject : <T = never>(reason?: any) => Promise<T>
86+
>Promise : PromiseConstructor
87+
>reject : <T = never>(reason?: any) => Promise<T>
88+
89+
h(async v => v ? (def) => { } : Promise.reject());
90+
>h(async v => v ? (def) => { } : Promise.reject()) : void
91+
>h : (cb: (v: boolean) => MyCallback | PromiseLike<MyCallback>) => void
92+
>async v => v ? (def) => { } : Promise.reject() : (v: boolean) => Promise<MyCallback | ((def: string) => void)>
93+
>v : boolean
94+
>v ? (def) => { } : Promise.reject() : Promise<MyCallback> | ((def: string) => void)
95+
>v : boolean
96+
>(def) => { } : (def: string) => void
97+
>def : string
98+
>Promise.reject() : Promise<MyCallback>
99+
>Promise.reject : <T = never>(reason?: any) => Promise<T>
100+
>Promise : PromiseConstructor
101+
>reject : <T = never>(reason?: any) => Promise<T>
102+
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
// @target: es6
2+
// @strict: true
3+
declare function f(cb: (v: boolean) => [0] | PromiseLike<[0]>): void;
4+
f(v => v ? [0] : Promise.reject());
5+
f(async v => v ? [0] : Promise.reject());
6+
7+
declare function g(cb: (v: boolean) => "contextuallyTypable" | PromiseLike<"contextuallyTypable">): void;
8+
g(v => v ? "contextuallyTypable" : Promise.reject());
9+
g(async v => v ? "contextuallyTypable" : Promise.reject());
10+
11+
type MyCallback = (thing: string) => void;
12+
declare function h(cb: (v: boolean) => MyCallback | PromiseLike<MyCallback>): void;
13+
h(v => v ? (abc) => { } : Promise.reject());
14+
h(async v => v ? (def) => { } : Promise.reject());

0 commit comments

Comments
 (0)