Skip to content

Commit c5b2884

Browse files
authored
Don't error when function has an implicit return but its return type is assignable to undefined (microsoft#53490)
1 parent 437fd05 commit c5b2884

10 files changed

+663
-6
lines changed

src/compiler/checker.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -35658,8 +35658,8 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker {
3565835658
const functionFlags = getFunctionFlags(func);
3565935659
const type = returnType && unwrapReturnType(returnType, functionFlags);
3566035660

35661-
// Functions with an explicitly specified 'undefined, 'void' or 'any' return type don't need any return expressions.
35662-
if (type && maybeTypeOfKind(type, TypeFlags.Undefined | TypeFlags.Void | TypeFlags.Any)) {
35661+
// Functions with an explicitly specified 'undefined, 'void', 'any' or 'unknown' return type don't need any return expressions.
35662+
if (type && maybeTypeOfKind(type, TypeFlags.Undefined | TypeFlags.Void | TypeFlags.Any | TypeFlags.Unknown)) {
3566335663
return;
3566435664
}
3566535665

Original file line numberDiff line numberDiff line change
@@ -0,0 +1,85 @@
1+
//// [functionsWithImplicitReturnTypeAssignableToUndefined.ts]
2+
function f1(): unknown {
3+
if (Math.random() < 0.5) return true;
4+
5+
// Implicit return, but undefined is always assignable to unknown.
6+
}
7+
8+
type MyUnknown = unknown;
9+
function f2(): unknown {
10+
if (Math.random() < 0.5) return true;
11+
12+
// Implicit return, but undefined is always assignable to unknown.
13+
}
14+
15+
function f3(): any {
16+
// Implicit return, but undefined is always assignable to any.
17+
}
18+
19+
function f4(): void {
20+
// Implicit return, but undefined is always assignable to void.
21+
}
22+
23+
function f5(): {} {
24+
if (Math.random() < 0.5) return {};
25+
26+
// Implicit return, but undefined is assignable to object when strictNullChecks is off.
27+
}
28+
29+
function f6(): Record<string, any> {
30+
if (Math.random() < 0.5) return { "foo": true };
31+
32+
// Implicit return, but undefined is assignable to records (which are just fancy objects)
33+
// when strictNullChecks is off.
34+
}
35+
36+
function f7(): null {
37+
if (Math.random() < 0.5) return null;
38+
39+
// Implicit return, but undefined is assignable to null when strictNullChecks is off.
40+
}
41+
42+
function f8(): string | null {
43+
if (Math.random() < 0.5) return "foo";
44+
45+
// Implicit return, but undefined is assignable to null when strictNullChecks is off.
46+
}
47+
48+
//// [functionsWithImplicitReturnTypeAssignableToUndefined.js]
49+
function f1() {
50+
if (Math.random() < 0.5)
51+
return true;
52+
// Implicit return, but undefined is always assignable to unknown.
53+
}
54+
function f2() {
55+
if (Math.random() < 0.5)
56+
return true;
57+
// Implicit return, but undefined is always assignable to unknown.
58+
}
59+
function f3() {
60+
// Implicit return, but undefined is always assignable to any.
61+
}
62+
function f4() {
63+
// Implicit return, but undefined is always assignable to void.
64+
}
65+
function f5() {
66+
if (Math.random() < 0.5)
67+
return {};
68+
// Implicit return, but undefined is assignable to object when strictNullChecks is off.
69+
}
70+
function f6() {
71+
if (Math.random() < 0.5)
72+
return { "foo": true };
73+
// Implicit return, but undefined is assignable to records (which are just fancy objects)
74+
// when strictNullChecks is off.
75+
}
76+
function f7() {
77+
if (Math.random() < 0.5)
78+
return null;
79+
// Implicit return, but undefined is assignable to null when strictNullChecks is off.
80+
}
81+
function f8() {
82+
if (Math.random() < 0.5)
83+
return "foo";
84+
// Implicit return, but undefined is assignable to null when strictNullChecks is off.
85+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,84 @@
1+
=== tests/cases/compiler/functionsWithImplicitReturnTypeAssignableToUndefined.ts ===
2+
function f1(): unknown {
3+
>f1 : Symbol(f1, Decl(functionsWithImplicitReturnTypeAssignableToUndefined.ts, 0, 0))
4+
5+
if (Math.random() < 0.5) return true;
6+
>Math.random : Symbol(Math.random, Decl(lib.es5.d.ts, --, --))
7+
>Math : Symbol(Math, Decl(lib.es5.d.ts, --, --), Decl(lib.es5.d.ts, --, --))
8+
>random : Symbol(Math.random, Decl(lib.es5.d.ts, --, --))
9+
10+
// Implicit return, but undefined is always assignable to unknown.
11+
}
12+
13+
type MyUnknown = unknown;
14+
>MyUnknown : Symbol(MyUnknown, Decl(functionsWithImplicitReturnTypeAssignableToUndefined.ts, 4, 1))
15+
16+
function f2(): unknown {
17+
>f2 : Symbol(f2, Decl(functionsWithImplicitReturnTypeAssignableToUndefined.ts, 6, 25))
18+
19+
if (Math.random() < 0.5) return true;
20+
>Math.random : Symbol(Math.random, Decl(lib.es5.d.ts, --, --))
21+
>Math : Symbol(Math, Decl(lib.es5.d.ts, --, --), Decl(lib.es5.d.ts, --, --))
22+
>random : Symbol(Math.random, Decl(lib.es5.d.ts, --, --))
23+
24+
// Implicit return, but undefined is always assignable to unknown.
25+
}
26+
27+
function f3(): any {
28+
>f3 : Symbol(f3, Decl(functionsWithImplicitReturnTypeAssignableToUndefined.ts, 11, 1))
29+
30+
// Implicit return, but undefined is always assignable to any.
31+
}
32+
33+
function f4(): void {
34+
>f4 : Symbol(f4, Decl(functionsWithImplicitReturnTypeAssignableToUndefined.ts, 15, 1))
35+
36+
// Implicit return, but undefined is always assignable to void.
37+
}
38+
39+
function f5(): {} {
40+
>f5 : Symbol(f5, Decl(functionsWithImplicitReturnTypeAssignableToUndefined.ts, 19, 1))
41+
42+
if (Math.random() < 0.5) return {};
43+
>Math.random : Symbol(Math.random, Decl(lib.es5.d.ts, --, --))
44+
>Math : Symbol(Math, Decl(lib.es5.d.ts, --, --), Decl(lib.es5.d.ts, --, --))
45+
>random : Symbol(Math.random, Decl(lib.es5.d.ts, --, --))
46+
47+
// Implicit return, but undefined is assignable to object when strictNullChecks is off.
48+
}
49+
50+
function f6(): Record<string, any> {
51+
>f6 : Symbol(f6, Decl(functionsWithImplicitReturnTypeAssignableToUndefined.ts, 25, 1))
52+
>Record : Symbol(Record, Decl(lib.es5.d.ts, --, --))
53+
54+
if (Math.random() < 0.5) return { "foo": true };
55+
>Math.random : Symbol(Math.random, Decl(lib.es5.d.ts, --, --))
56+
>Math : Symbol(Math, Decl(lib.es5.d.ts, --, --), Decl(lib.es5.d.ts, --, --))
57+
>random : Symbol(Math.random, Decl(lib.es5.d.ts, --, --))
58+
>"foo" : Symbol("foo", Decl(functionsWithImplicitReturnTypeAssignableToUndefined.ts, 28, 37))
59+
60+
// Implicit return, but undefined is assignable to records (which are just fancy objects)
61+
// when strictNullChecks is off.
62+
}
63+
64+
function f7(): null {
65+
>f7 : Symbol(f7, Decl(functionsWithImplicitReturnTypeAssignableToUndefined.ts, 32, 1))
66+
67+
if (Math.random() < 0.5) return null;
68+
>Math.random : Symbol(Math.random, Decl(lib.es5.d.ts, --, --))
69+
>Math : Symbol(Math, Decl(lib.es5.d.ts, --, --), Decl(lib.es5.d.ts, --, --))
70+
>random : Symbol(Math.random, Decl(lib.es5.d.ts, --, --))
71+
72+
// Implicit return, but undefined is assignable to null when strictNullChecks is off.
73+
}
74+
75+
function f8(): string | null {
76+
>f8 : Symbol(f8, Decl(functionsWithImplicitReturnTypeAssignableToUndefined.ts, 38, 1))
77+
78+
if (Math.random() < 0.5) return "foo";
79+
>Math.random : Symbol(Math.random, Decl(lib.es5.d.ts, --, --))
80+
>Math : Symbol(Math, Decl(lib.es5.d.ts, --, --), Decl(lib.es5.d.ts, --, --))
81+
>random : Symbol(Math.random, Decl(lib.es5.d.ts, --, --))
82+
83+
// Implicit return, but undefined is assignable to null when strictNullChecks is off.
84+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,107 @@
1+
=== tests/cases/compiler/functionsWithImplicitReturnTypeAssignableToUndefined.ts ===
2+
function f1(): unknown {
3+
>f1 : () => unknown
4+
5+
if (Math.random() < 0.5) return true;
6+
>Math.random() < 0.5 : boolean
7+
>Math.random() : number
8+
>Math.random : () => number
9+
>Math : Math
10+
>random : () => number
11+
>0.5 : 0.5
12+
>true : true
13+
14+
// Implicit return, but undefined is always assignable to unknown.
15+
}
16+
17+
type MyUnknown = unknown;
18+
>MyUnknown : unknown
19+
20+
function f2(): unknown {
21+
>f2 : () => unknown
22+
23+
if (Math.random() < 0.5) return true;
24+
>Math.random() < 0.5 : boolean
25+
>Math.random() : number
26+
>Math.random : () => number
27+
>Math : Math
28+
>random : () => number
29+
>0.5 : 0.5
30+
>true : true
31+
32+
// Implicit return, but undefined is always assignable to unknown.
33+
}
34+
35+
function f3(): any {
36+
>f3 : () => any
37+
38+
// Implicit return, but undefined is always assignable to any.
39+
}
40+
41+
function f4(): void {
42+
>f4 : () => void
43+
44+
// Implicit return, but undefined is always assignable to void.
45+
}
46+
47+
function f5(): {} {
48+
>f5 : () => {}
49+
50+
if (Math.random() < 0.5) return {};
51+
>Math.random() < 0.5 : boolean
52+
>Math.random() : number
53+
>Math.random : () => number
54+
>Math : Math
55+
>random : () => number
56+
>0.5 : 0.5
57+
>{} : {}
58+
59+
// Implicit return, but undefined is assignable to object when strictNullChecks is off.
60+
}
61+
62+
function f6(): Record<string, any> {
63+
>f6 : () => Record<string, any>
64+
65+
if (Math.random() < 0.5) return { "foo": true };
66+
>Math.random() < 0.5 : boolean
67+
>Math.random() : number
68+
>Math.random : () => number
69+
>Math : Math
70+
>random : () => number
71+
>0.5 : 0.5
72+
>{ "foo": true } : { foo: boolean; }
73+
>"foo" : boolean
74+
>true : true
75+
76+
// Implicit return, but undefined is assignable to records (which are just fancy objects)
77+
// when strictNullChecks is off.
78+
}
79+
80+
function f7(): null {
81+
>f7 : () => null
82+
83+
if (Math.random() < 0.5) return null;
84+
>Math.random() < 0.5 : boolean
85+
>Math.random() : number
86+
>Math.random : () => number
87+
>Math : Math
88+
>random : () => number
89+
>0.5 : 0.5
90+
91+
// Implicit return, but undefined is assignable to null when strictNullChecks is off.
92+
}
93+
94+
function f8(): string | null {
95+
>f8 : () => string | null
96+
97+
if (Math.random() < 0.5) return "foo";
98+
>Math.random() < 0.5 : boolean
99+
>Math.random() : number
100+
>Math.random : () => number
101+
>Math : Math
102+
>random : () => number
103+
>0.5 : 0.5
104+
>"foo" : "foo"
105+
106+
// Implicit return, but undefined is assignable to null when strictNullChecks is off.
107+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,60 @@
1+
tests/cases/compiler/functionsWithImplicitReturnTypeAssignableToUndefined.ts(22,16): error TS2366: Function lacks ending return statement and return type does not include 'undefined'.
2+
tests/cases/compiler/functionsWithImplicitReturnTypeAssignableToUndefined.ts(28,16): error TS2366: Function lacks ending return statement and return type does not include 'undefined'.
3+
tests/cases/compiler/functionsWithImplicitReturnTypeAssignableToUndefined.ts(35,16): error TS2366: Function lacks ending return statement and return type does not include 'undefined'.
4+
tests/cases/compiler/functionsWithImplicitReturnTypeAssignableToUndefined.ts(41,16): error TS2366: Function lacks ending return statement and return type does not include 'undefined'.
5+
6+
7+
==== tests/cases/compiler/functionsWithImplicitReturnTypeAssignableToUndefined.ts (4 errors) ====
8+
function f1(): unknown {
9+
if (Math.random() < 0.5) return true;
10+
11+
// Implicit return, but undefined is always assignable to unknown.
12+
}
13+
14+
type MyUnknown = unknown;
15+
function f2(): unknown {
16+
if (Math.random() < 0.5) return true;
17+
18+
// Implicit return, but undefined is always assignable to unknown.
19+
}
20+
21+
function f3(): any {
22+
// Implicit return, but undefined is always assignable to any.
23+
}
24+
25+
function f4(): void {
26+
// Implicit return, but undefined is always assignable to void.
27+
}
28+
29+
function f5(): {} {
30+
~~
31+
!!! error TS2366: Function lacks ending return statement and return type does not include 'undefined'.
32+
if (Math.random() < 0.5) return {};
33+
34+
// Implicit return, but undefined is assignable to object when strictNullChecks is off.
35+
}
36+
37+
function f6(): Record<string, any> {
38+
~~~~~~~~~~~~~~~~~~~
39+
!!! error TS2366: Function lacks ending return statement and return type does not include 'undefined'.
40+
if (Math.random() < 0.5) return { "foo": true };
41+
42+
// Implicit return, but undefined is assignable to records (which are just fancy objects)
43+
// when strictNullChecks is off.
44+
}
45+
46+
function f7(): null {
47+
~~~~
48+
!!! error TS2366: Function lacks ending return statement and return type does not include 'undefined'.
49+
if (Math.random() < 0.5) return null;
50+
51+
// Implicit return, but undefined is assignable to null when strictNullChecks is off.
52+
}
53+
54+
function f8(): string | null {
55+
~~~~~~~~~~~~~
56+
!!! error TS2366: Function lacks ending return statement and return type does not include 'undefined'.
57+
if (Math.random() < 0.5) return "foo";
58+
59+
// Implicit return, but undefined is assignable to null when strictNullChecks is off.
60+
}

0 commit comments

Comments
 (0)