Skip to content

Commit 563593b

Browse files
authored
Enable better error spans for async functions (#30413)
1 parent 81ed511 commit 563593b

6 files changed

+213
-1
lines changed

src/compiler/checker.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26911,7 +26911,7 @@ namespace ts {
2691126911
// If the function has a return type, but promisedType is
2691226912
// undefined, an error will be reported in checkAsyncFunctionReturnType
2691326913
// so we don't need to report one here.
26914-
checkTypeAssignableTo(awaitedType, promisedType, node);
26914+
checkTypeAssignableToAndOptionallyElaborate(awaitedType, promisedType, node, node.expression);
2691526915
}
2691626916
}
2691726917
else {
Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
tests/cases/compiler/asyncFunctionReturnExpressionErrorSpans.ts(11,28): error TS2705: An async function or method in ES5/ES3 requires the 'Promise' constructor. Make sure you have a declaration for the 'Promise' constructor or include 'ES2015' in your `--lib` option.
2+
tests/cases/compiler/asyncFunctionReturnExpressionErrorSpans.ts(16,21): error TS2322: Type 'number' is not assignable to type 'string'.
3+
4+
5+
==== tests/cases/compiler/asyncFunctionReturnExpressionErrorSpans.ts (2 errors) ====
6+
interface Foo {
7+
bar: {
8+
baz: {
9+
inner: {
10+
thing: string
11+
}
12+
}
13+
}
14+
}
15+
16+
async function asyncFoo(): Promise<Foo> {
17+
~~~~~~~~~~~~
18+
!!! error TS2705: An async function or method in ES5/ES3 requires the 'Promise' constructor. Make sure you have a declaration for the 'Promise' constructor or include 'ES2015' in your `--lib` option.
19+
return {
20+
bar: {
21+
baz: {
22+
inner: {
23+
thing: 1
24+
~~~~~
25+
!!! error TS2322: Type 'number' is not assignable to type 'string'.
26+
!!! related TS6500 tests/cases/compiler/asyncFunctionReturnExpressionErrorSpans.ts:5:17: The expected type comes from property 'thing' which is declared here on type '{ thing: string; }'
27+
}
28+
}
29+
}
30+
}
31+
}
Lines changed: 74 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,74 @@
1+
//// [asyncFunctionReturnExpressionErrorSpans.ts]
2+
interface Foo {
3+
bar: {
4+
baz: {
5+
inner: {
6+
thing: string
7+
}
8+
}
9+
}
10+
}
11+
12+
async function asyncFoo(): Promise<Foo> {
13+
return {
14+
bar: {
15+
baz: {
16+
inner: {
17+
thing: 1
18+
}
19+
}
20+
}
21+
}
22+
}
23+
24+
//// [asyncFunctionReturnExpressionErrorSpans.js]
25+
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
26+
return new (P || (P = Promise))(function (resolve, reject) {
27+
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
28+
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
29+
function step(result) { result.done ? resolve(result.value) : new P(function (resolve) { resolve(result.value); }).then(fulfilled, rejected); }
30+
step((generator = generator.apply(thisArg, _arguments || [])).next());
31+
});
32+
};
33+
var __generator = (this && this.__generator) || function (thisArg, body) {
34+
var _ = { label: 0, sent: function() { if (t[0] & 1) throw t[1]; return t[1]; }, trys: [], ops: [] }, f, y, t, g;
35+
return g = { next: verb(0), "throw": verb(1), "return": verb(2) }, typeof Symbol === "function" && (g[Symbol.iterator] = function() { return this; }), g;
36+
function verb(n) { return function (v) { return step([n, v]); }; }
37+
function step(op) {
38+
if (f) throw new TypeError("Generator is already executing.");
39+
while (_) try {
40+
if (f = 1, y && (t = op[0] & 2 ? y["return"] : op[0] ? y["throw"] || ((t = y["return"]) && t.call(y), 0) : y.next) && !(t = t.call(y, op[1])).done) return t;
41+
if (y = 0, t) op = [op[0] & 2, t.value];
42+
switch (op[0]) {
43+
case 0: case 1: t = op; break;
44+
case 4: _.label++; return { value: op[1], done: false };
45+
case 5: _.label++; y = op[1]; op = [0]; continue;
46+
case 7: op = _.ops.pop(); _.trys.pop(); continue;
47+
default:
48+
if (!(t = _.trys, t = t.length > 0 && t[t.length - 1]) && (op[0] === 6 || op[0] === 2)) { _ = 0; continue; }
49+
if (op[0] === 3 && (!t || (op[1] > t[0] && op[1] < t[3]))) { _.label = op[1]; break; }
50+
if (op[0] === 6 && _.label < t[1]) { _.label = t[1]; t = op; break; }
51+
if (t && _.label < t[2]) { _.label = t[2]; _.ops.push(op); break; }
52+
if (t[2]) _.ops.pop();
53+
_.trys.pop(); continue;
54+
}
55+
op = body.call(thisArg, _);
56+
} catch (e) { op = [6, e]; y = 0; } finally { f = t = 0; }
57+
if (op[0] & 5) throw op[1]; return { value: op[0] ? op[1] : void 0, done: true };
58+
}
59+
};
60+
function asyncFoo() {
61+
return __awaiter(this, void 0, void 0, function () {
62+
return __generator(this, function (_a) {
63+
return [2 /*return*/, {
64+
bar: {
65+
baz: {
66+
inner: {
67+
thing: 1
68+
}
69+
}
70+
}
71+
}];
72+
});
73+
});
74+
}
Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
=== tests/cases/compiler/asyncFunctionReturnExpressionErrorSpans.ts ===
2+
interface Foo {
3+
>Foo : Symbol(Foo, Decl(asyncFunctionReturnExpressionErrorSpans.ts, 0, 0))
4+
5+
bar: {
6+
>bar : Symbol(Foo.bar, Decl(asyncFunctionReturnExpressionErrorSpans.ts, 0, 15))
7+
8+
baz: {
9+
>baz : Symbol(baz, Decl(asyncFunctionReturnExpressionErrorSpans.ts, 1, 10))
10+
11+
inner: {
12+
>inner : Symbol(inner, Decl(asyncFunctionReturnExpressionErrorSpans.ts, 2, 14))
13+
14+
thing: string
15+
>thing : Symbol(thing, Decl(asyncFunctionReturnExpressionErrorSpans.ts, 3, 20))
16+
}
17+
}
18+
}
19+
}
20+
21+
async function asyncFoo(): Promise<Foo> {
22+
>asyncFoo : Symbol(asyncFoo, Decl(asyncFunctionReturnExpressionErrorSpans.ts, 8, 1))
23+
>Promise : Symbol(Promise, Decl(lib.es5.d.ts, --, --))
24+
>Foo : Symbol(Foo, Decl(asyncFunctionReturnExpressionErrorSpans.ts, 0, 0))
25+
26+
return {
27+
bar: {
28+
>bar : Symbol(bar, Decl(asyncFunctionReturnExpressionErrorSpans.ts, 11, 12))
29+
30+
baz: {
31+
>baz : Symbol(baz, Decl(asyncFunctionReturnExpressionErrorSpans.ts, 12, 14))
32+
33+
inner: {
34+
>inner : Symbol(inner, Decl(asyncFunctionReturnExpressionErrorSpans.ts, 13, 18))
35+
36+
thing: 1
37+
>thing : Symbol(thing, Decl(asyncFunctionReturnExpressionErrorSpans.ts, 14, 24))
38+
}
39+
}
40+
}
41+
}
42+
}
Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
=== tests/cases/compiler/asyncFunctionReturnExpressionErrorSpans.ts ===
2+
interface Foo {
3+
bar: {
4+
>bar : { baz: { inner: { thing: string; }; }; }
5+
6+
baz: {
7+
>baz : { inner: { thing: string; }; }
8+
9+
inner: {
10+
>inner : { thing: string; }
11+
12+
thing: string
13+
>thing : string
14+
}
15+
}
16+
}
17+
}
18+
19+
async function asyncFoo(): Promise<Foo> {
20+
>asyncFoo : () => Promise<Foo>
21+
22+
return {
23+
>{ bar: { baz: { inner: { thing: 1 } } } } : { bar: { baz: { inner: { thing: number; }; }; }; }
24+
25+
bar: {
26+
>bar : { baz: { inner: { thing: number; }; }; }
27+
>{ baz: { inner: { thing: 1 } } } : { baz: { inner: { thing: number; }; }; }
28+
29+
baz: {
30+
>baz : { inner: { thing: number; }; }
31+
>{ inner: { thing: 1 } } : { inner: { thing: number; }; }
32+
33+
inner: {
34+
>inner : { thing: number; }
35+
>{ thing: 1 } : { thing: number; }
36+
37+
thing: 1
38+
>thing : number
39+
>1 : 1
40+
}
41+
}
42+
}
43+
}
44+
}
Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
interface Foo {
2+
bar: {
3+
baz: {
4+
inner: {
5+
thing: string
6+
}
7+
}
8+
}
9+
}
10+
11+
async function asyncFoo(): Promise<Foo> {
12+
return {
13+
bar: {
14+
baz: {
15+
inner: {
16+
thing: 1
17+
}
18+
}
19+
}
20+
}
21+
}

0 commit comments

Comments
 (0)