Skip to content

Commit 74e2154

Browse files
committed
Merge pull request #8767 from Microsoft/neverTypeInference
Only infer 'never' in function expressions and arrow functions
2 parents 1527499 + 10d331e commit 74e2154

14 files changed

+314
-133
lines changed

src/compiler/checker.ts

Lines changed: 18 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -11586,7 +11586,7 @@ namespace ts {
1158611586
let types: Type[];
1158711587
const funcIsGenerator = !!func.asteriskToken;
1158811588
if (funcIsGenerator) {
11589-
types = checkAndAggregateYieldOperandTypes(<Block>func.body, contextualMapper);
11589+
types = checkAndAggregateYieldOperandTypes(func, contextualMapper);
1159011590
if (types.length === 0) {
1159111591
const iterableIteratorAny = createIterableIteratorType(anyType);
1159211592
if (compilerOptions.noImplicitAny) {
@@ -11597,8 +11597,7 @@ namespace ts {
1159711597
}
1159811598
}
1159911599
else {
11600-
const hasImplicitReturn = !!(func.flags & NodeFlags.HasImplicitReturn);
11601-
types = checkAndAggregateReturnExpressionTypes(<Block>func.body, contextualMapper, isAsync, hasImplicitReturn);
11600+
types = checkAndAggregateReturnExpressionTypes(func, contextualMapper);
1160211601
if (!types) {
1160311602
return neverType;
1160411603
}
@@ -11656,10 +11655,10 @@ namespace ts {
1165611655
}
1165711656
}
1165811657

11659-
function checkAndAggregateYieldOperandTypes(body: Block, contextualMapper?: TypeMapper): Type[] {
11658+
function checkAndAggregateYieldOperandTypes(func: FunctionLikeDeclaration, contextualMapper: TypeMapper): Type[] {
1166011659
const aggregatedTypes: Type[] = [];
1166111660

11662-
forEachYieldExpression(body, yieldExpression => {
11661+
forEachYieldExpression(<Block>func.body, yieldExpression => {
1166311662
const expr = yieldExpression.expression;
1166411663
if (expr) {
1166511664
let type = checkExpressionCached(expr, contextualMapper);
@@ -11678,10 +11677,12 @@ namespace ts {
1167811677
return aggregatedTypes;
1167911678
}
1168011679

11681-
function checkAndAggregateReturnExpressionTypes(body: Block, contextualMapper: TypeMapper, isAsync: boolean, hasImplicitReturn: boolean): Type[] {
11680+
function checkAndAggregateReturnExpressionTypes(func: FunctionLikeDeclaration, contextualMapper: TypeMapper): Type[] {
11681+
const isAsync = isAsyncFunctionLike(func);
1168211682
const aggregatedTypes: Type[] = [];
11683-
let hasOmittedExpressions = false;
11684-
forEachReturnStatement(body, returnStatement => {
11683+
let hasReturnWithNoExpression = !!(func.flags & NodeFlags.HasImplicitReturn);
11684+
let hasReturnOfTypeNever = false;
11685+
forEachReturnStatement(<Block>func.body, returnStatement => {
1168511686
const expr = returnStatement.expression;
1168611687
if (expr) {
1168711688
let type = checkExpressionCached(expr, contextualMapper);
@@ -11690,20 +11691,24 @@ namespace ts {
1169011691
// Promise/A+ compatible implementation will always assimilate any foreign promise, so the
1169111692
// return type of the body should be unwrapped to its awaited type, which should be wrapped in
1169211693
// the native Promise<T> type by the caller.
11693-
type = checkAwaitedType(type, body.parent, Diagnostics.Return_expression_in_async_function_does_not_have_a_valid_callable_then_member);
11694+
type = checkAwaitedType(type, func, Diagnostics.Return_expression_in_async_function_does_not_have_a_valid_callable_then_member);
11695+
}
11696+
if (type === neverType) {
11697+
hasReturnOfTypeNever = true;
1169411698
}
11695-
if (type !== neverType && !contains(aggregatedTypes, type)) {
11699+
else if (!contains(aggregatedTypes, type)) {
1169611700
aggregatedTypes.push(type);
1169711701
}
1169811702
}
1169911703
else {
11700-
hasOmittedExpressions = true;
11704+
hasReturnWithNoExpression = true;
1170111705
}
1170211706
});
11703-
if (aggregatedTypes.length === 0 && !hasOmittedExpressions && !hasImplicitReturn) {
11707+
if (aggregatedTypes.length === 0 && !hasReturnWithNoExpression && (hasReturnOfTypeNever ||
11708+
func.kind === SyntaxKind.FunctionExpression || func.kind === SyntaxKind.ArrowFunction)) {
1170411709
return undefined;
1170511710
}
11706-
if (strictNullChecks && aggregatedTypes.length && (hasOmittedExpressions || hasImplicitReturn)) {
11711+
if (strictNullChecks && aggregatedTypes.length && hasReturnWithNoExpression) {
1170711712
if (!contains(aggregatedTypes, undefinedType)) {
1170811713
aggregatedTypes.push(undefinedType);
1170911714
}

tests/baselines/reference/controlFlowIteration.types

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ let cond: boolean;
44
>cond : boolean
55

66
function ff() {
7-
>ff : () => never
7+
>ff : () => void
88

99
let x: string | undefined;
1010
>x : string | undefined

tests/baselines/reference/duplicateLabel3.types

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ while (true) {
77
>true : boolean
88

99
function f() {
10-
>f : () => never
10+
>f : () => void
1111

1212
target:
1313
>target : any

tests/baselines/reference/forStatementsMultipleValidDecl.types

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ for (var x = <number>undefined; ;) { }
1616

1717
// new declaration space, making redeclaring x as a string valid
1818
function declSpace() {
19-
>declSpace : () => never
19+
>declSpace : () => void
2020

2121
for (var x = 'this is a string'; ;) { }
2222
>x : string

tests/baselines/reference/narrowingOfDottedNames.types

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -42,7 +42,7 @@ function isB(x: any): x is B {
4242
}
4343

4444
function f1(x: A | B) {
45-
>f1 : (x: A | B) => never
45+
>f1 : (x: A | B) => void
4646
>x : A | B
4747
>A : A
4848
>B : B
@@ -78,7 +78,7 @@ function f1(x: A | B) {
7878
}
7979

8080
function f2(x: A | B) {
81-
>f2 : (x: A | B) => never
81+
>f2 : (x: A | B) => void
8282
>x : A | B
8383
>A : A
8484
>B : B

tests/baselines/reference/nestedBlockScopedBindings3.types

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
=== tests/cases/compiler/nestedBlockScopedBindings3.ts ===
22
function a0() {
3-
>a0 : () => never
3+
>a0 : () => void
44
{
55
for (let x = 0; x < 1; ) {
66
>x : number
@@ -26,7 +26,7 @@ function a0() {
2626
}
2727

2828
function a1() {
29-
>a1 : () => never
29+
>a1 : () => void
3030

3131
for (let x; x < 1;) {
3232
>x : any
@@ -48,7 +48,7 @@ function a1() {
4848
}
4949

5050
function a2() {
51-
>a2 : () => never
51+
>a2 : () => void
5252

5353
for (let x; x < 1;) {
5454
>x : any

tests/baselines/reference/nestedBlockScopedBindings4.types

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
=== tests/cases/compiler/nestedBlockScopedBindings4.ts ===
22
function a0() {
3-
>a0 : () => never
3+
>a0 : () => void
44

55
for (let x; x < 1;) {
66
>x : any
@@ -28,7 +28,7 @@ function a0() {
2828
}
2929

3030
function a1() {
31-
>a1 : () => never
31+
>a1 : () => void
3232

3333
for (let x; x < 1;) {
3434
>x : any
@@ -60,7 +60,7 @@ function a1() {
6060
}
6161

6262
function a2() {
63-
>a2 : () => never
63+
>a2 : () => void
6464

6565
for (let x; x < 1;) {
6666
>x : any
@@ -93,7 +93,7 @@ function a2() {
9393

9494

9595
function a3() {
96-
>a3 : () => never
96+
>a3 : () => void
9797

9898
for (let x; x < 1;) {
9999
>x : any

tests/baselines/reference/nestedBlockScopedBindings6.types

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
=== tests/cases/compiler/nestedBlockScopedBindings6.ts ===
22
function a0() {
3-
>a0 : () => never
3+
>a0 : () => void
44

55
for (let x of [1]) {
66
>x : number
@@ -27,7 +27,7 @@ function a0() {
2727
}
2828

2929
function a1() {
30-
>a1 : () => never
30+
>a1 : () => void
3131

3232
for (let x of [1]) {
3333
>x : number
@@ -58,7 +58,7 @@ function a1() {
5858
}
5959

6060
function a2() {
61-
>a2 : () => never
61+
>a2 : () => void
6262

6363
for (let x of [1]) {
6464
>x : number
@@ -89,7 +89,7 @@ function a2() {
8989
}
9090

9191
function a3() {
92-
>a3 : () => never
92+
>a3 : () => void
9393

9494
for (let x of [1]) {
9595
>x : number

tests/baselines/reference/neverType.js

Lines changed: 75 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,31 @@
11
//// [neverType.ts]
22

3-
function error(message: string) {
3+
4+
function error(message: string): never {
5+
throw new Error(message);
6+
}
7+
8+
function errorVoid(message: string) {
49
throw new Error(message);
510
}
611

712
function fail() {
813
return error("Something failed");
914
}
1015

11-
function infiniteLoop() {
16+
function failOrThrow(shouldFail: boolean) {
17+
if (shouldFail) {
18+
return fail();
19+
}
20+
throw new Error();
21+
}
22+
23+
function infiniteLoop1() {
24+
while (true) {
25+
}
26+
}
27+
28+
function infiniteLoop2(): never {
1229
while (true) {
1330
}
1431
}
@@ -33,6 +50,21 @@ function check<T>(x: T | undefined) {
3350
return x || error("Undefined value");
3451
}
3552

53+
class C {
54+
void1() {
55+
throw new Error();
56+
}
57+
void2() {
58+
while (true) {}
59+
}
60+
never1(): never {
61+
throw new Error();
62+
}
63+
never2(): never {
64+
while (true) {}
65+
}
66+
}
67+
3668
function f1(x: string | number) {
3769
if (typeof x === "boolean") {
3870
x; // never
@@ -47,13 +79,6 @@ function f2(x: string | number) {
4779
}
4880
}
4981

50-
function failOrThrow(shouldFail: boolean) {
51-
if (shouldFail) {
52-
return fail();
53-
}
54-
throw new Error();
55-
}
56-
5782
function test(cb: () => string) {
5883
let s = cb();
5984
return s;
@@ -71,10 +96,23 @@ test(errorCallback);
7196
function error(message) {
7297
throw new Error(message);
7398
}
99+
function errorVoid(message) {
100+
throw new Error(message);
101+
}
74102
function fail() {
75103
return error("Something failed");
76104
}
77-
function infiniteLoop() {
105+
function failOrThrow(shouldFail) {
106+
if (shouldFail) {
107+
return fail();
108+
}
109+
throw new Error();
110+
}
111+
function infiniteLoop1() {
112+
while (true) {
113+
}
114+
}
115+
function infiniteLoop2() {
78116
while (true) {
79117
}
80118
}
@@ -95,6 +133,23 @@ function move2(direction) {
95133
function check(x) {
96134
return x || error("Undefined value");
97135
}
136+
var C = (function () {
137+
function C() {
138+
}
139+
C.prototype.void1 = function () {
140+
throw new Error();
141+
};
142+
C.prototype.void2 = function () {
143+
while (true) { }
144+
};
145+
C.prototype.never1 = function () {
146+
throw new Error();
147+
};
148+
C.prototype.never2 = function () {
149+
while (true) { }
150+
};
151+
return C;
152+
}());
98153
function f1(x) {
99154
if (typeof x === "boolean") {
100155
x; // never
@@ -107,12 +162,6 @@ function f2(x) {
107162
}
108163
}
109164
}
110-
function failOrThrow(shouldFail) {
111-
if (shouldFail) {
112-
return fail();
113-
}
114-
throw new Error();
115-
}
116165
function test(cb) {
117166
var s = cb();
118167
return s;
@@ -126,13 +175,21 @@ test(errorCallback);
126175

127176
//// [neverType.d.ts]
128177
declare function error(message: string): never;
178+
declare function errorVoid(message: string): void;
129179
declare function fail(): never;
130-
declare function infiniteLoop(): never;
180+
declare function failOrThrow(shouldFail: boolean): never;
181+
declare function infiniteLoop1(): void;
182+
declare function infiniteLoop2(): never;
131183
declare function move1(direction: "up" | "down"): number;
132184
declare function move2(direction: "up" | "down"): number;
133185
declare function check<T>(x: T | undefined): T;
186+
declare class C {
187+
void1(): void;
188+
void2(): void;
189+
never1(): never;
190+
never2(): never;
191+
}
134192
declare function f1(x: string | number): void;
135193
declare function f2(x: string | number): never;
136-
declare function failOrThrow(shouldFail: boolean): never;
137194
declare function test(cb: () => string): string;
138195
declare let errorCallback: () => never;

0 commit comments

Comments
 (0)