Skip to content

Commit f6f4eab

Browse files
authored
Allow implicit undefined returns when the contextual union type contains it (#57912)
1 parent 2b2d6ce commit f6f4eab

7 files changed

+138
-23
lines changed

src/compiler/checker.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -39125,7 +39125,7 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker {
3912539125
if (types.length === 0) {
3912639126
// For an async function, the return type will not be void/undefined, but rather a Promise for void/undefined.
3912739127
const contextualReturnType = getContextualReturnType(func, /*contextFlags*/ undefined);
39128-
const returnType = contextualReturnType && (unwrapReturnType(contextualReturnType, functionFlags) || voidType).flags & TypeFlags.Undefined ? undefinedType : voidType;
39128+
const returnType = contextualReturnType && someType(unwrapReturnType(contextualReturnType, functionFlags) || voidType, t => !!(t.flags & TypeFlags.Undefined)) ? undefinedType : voidType;
3912939129
return functionFlags & FunctionFlags.Async ? createPromiseReturnType(func, returnType) : // Async function
3913039130
returnType; // Normal function
3913139131
}

tests/baselines/reference/functionsMissingReturnStatementsAndExpressionsStrictNullChecks.errors.txt

Lines changed: 18 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,5 @@
11
functionsMissingReturnStatementsAndExpressionsStrictNullChecks.ts(5,17): error TS2355: A function whose declared type is neither 'undefined', 'void', nor 'any' must return a value.
22
functionsMissingReturnStatementsAndExpressionsStrictNullChecks.ts(9,17): error TS2355: A function whose declared type is neither 'undefined', 'void', nor 'any' must return a value.
3-
functionsMissingReturnStatementsAndExpressionsStrictNullChecks.ts(17,7): error TS2322: Type '() => void' is not assignable to type '() => number | undefined'.
4-
Type 'void' is not assignable to type 'number | undefined'.
53
functionsMissingReturnStatementsAndExpressionsStrictNullChecks.ts(21,7): error TS2322: Type '() => void' is not assignable to type '() => number'.
64
Type 'void' is not assignable to type 'number'.
75
functionsMissingReturnStatementsAndExpressionsStrictNullChecks.ts(29,23): error TS2355: A function whose declared type is neither 'undefined', 'void', nor 'any' must return a value.
@@ -10,7 +8,7 @@ functionsMissingReturnStatementsAndExpressionsStrictNullChecks.ts(52,3): error T
108
Type 'void' is not assignable to type 'undefined'.
119

1210

13-
==== functionsMissingReturnStatementsAndExpressionsStrictNullChecks.ts (7 errors) ====
11+
==== functionsMissingReturnStatementsAndExpressionsStrictNullChecks.ts (6 errors) ====
1412
function f10(): undefined {
1513
// Ok, return type allows implicit return of undefined
1614
}
@@ -32,10 +30,7 @@ functionsMissingReturnStatementsAndExpressionsStrictNullChecks.ts(52,3): error T
3230
}
3331

3432
const f21: () => undefined | number = () => {
35-
~~~
36-
!!! error TS2322: Type '() => void' is not assignable to type '() => number | undefined'.
37-
!!! error TS2322: Type 'void' is not assignable to type 'number | undefined'.
38-
// Error, regular void function because contextual type for implicit return isn't just undefined
33+
// Ok, contextual type for implicit return contains undefined
3934
}
4035

4136
const f22: () => number = () => {
@@ -85,4 +80,20 @@ functionsMissingReturnStatementsAndExpressionsStrictNullChecks.ts(52,3): error T
8580
}
8681

8782
f(h2);
83+
84+
// https://github.com/microsoft/TypeScript/issues/57840
85+
86+
type FN = () => Promise<undefined> | undefined;
87+
88+
const fn1: FN = () => {
89+
return;
90+
};
91+
92+
const fn2: FN = async () => {
93+
return;
94+
};
95+
96+
const fn3: FN = () => {};
97+
98+
const fn4: FN = async () => {};
8899

tests/baselines/reference/functionsMissingReturnStatementsAndExpressionsStrictNullChecks.js

Lines changed: 26 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ const f20: () => undefined = () => {
1818
}
1919

2020
const f21: () => undefined | number = () => {
21-
// Error, regular void function because contextual type for implicit return isn't just undefined
21+
// Ok, contextual type for implicit return contains undefined
2222
}
2323

2424
const f22: () => number = () => {
@@ -58,6 +58,22 @@ function h2(): undefined {
5858
}
5959

6060
f(h2);
61+
62+
// https://github.com/microsoft/TypeScript/issues/57840
63+
64+
type FN = () => Promise<undefined> | undefined;
65+
66+
const fn1: FN = () => {
67+
return;
68+
};
69+
70+
const fn2: FN = async () => {
71+
return;
72+
};
73+
74+
const fn3: FN = () => {};
75+
76+
const fn4: FN = async () => {};
6177

6278

6379
//// [functionsMissingReturnStatementsAndExpressionsStrictNullChecks.js]
@@ -74,7 +90,7 @@ const f20 = () => {
7490
// Ok, contextual type for implicit return is undefined
7591
};
7692
const f21 = () => {
77-
// Error, regular void function because contextual type for implicit return isn't just undefined
93+
// Ok, contextual type for implicit return contains undefined
7894
};
7995
const f22 = () => {
8096
// Error, regular void function because contextual type for implicit return isn't just undefined
@@ -98,3 +114,11 @@ f(h1); // Error
98114
function h2() {
99115
}
100116
f(h2);
117+
const fn1 = () => {
118+
return;
119+
};
120+
const fn2 = async () => {
121+
return;
122+
};
123+
const fn3 = () => { };
124+
const fn4 = async () => { };

tests/baselines/reference/functionsMissingReturnStatementsAndExpressionsStrictNullChecks.symbols

Lines changed: 29 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,7 @@ const f20: () => undefined = () => {
2828
const f21: () => undefined | number = () => {
2929
>f21 : Symbol(f21, Decl(functionsMissingReturnStatementsAndExpressionsStrictNullChecks.ts, 16, 5))
3030

31-
// Error, regular void function because contextual type for implicit return isn't just undefined
31+
// Ok, contextual type for implicit return contains undefined
3232
}
3333

3434
const f22: () => number = () => {
@@ -92,3 +92,31 @@ f(h2);
9292
>f : Symbol(f, Decl(functionsMissingReturnStatementsAndExpressionsStrictNullChecks.ts, 34, 1))
9393
>h2 : Symbol(h2, Decl(functionsMissingReturnStatementsAndExpressionsStrictNullChecks.ts, 51, 6))
9494

95+
// https://github.com/microsoft/TypeScript/issues/57840
96+
97+
type FN = () => Promise<undefined> | undefined;
98+
>FN : Symbol(FN, Decl(functionsMissingReturnStatementsAndExpressionsStrictNullChecks.ts, 56, 6))
99+
>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, --, --))
100+
101+
const fn1: FN = () => {
102+
>fn1 : Symbol(fn1, Decl(functionsMissingReturnStatementsAndExpressionsStrictNullChecks.ts, 62, 5))
103+
>FN : Symbol(FN, Decl(functionsMissingReturnStatementsAndExpressionsStrictNullChecks.ts, 56, 6))
104+
105+
return;
106+
};
107+
108+
const fn2: FN = async () => {
109+
>fn2 : Symbol(fn2, Decl(functionsMissingReturnStatementsAndExpressionsStrictNullChecks.ts, 66, 5))
110+
>FN : Symbol(FN, Decl(functionsMissingReturnStatementsAndExpressionsStrictNullChecks.ts, 56, 6))
111+
112+
return;
113+
};
114+
115+
const fn3: FN = () => {};
116+
>fn3 : Symbol(fn3, Decl(functionsMissingReturnStatementsAndExpressionsStrictNullChecks.ts, 70, 5))
117+
>FN : Symbol(FN, Decl(functionsMissingReturnStatementsAndExpressionsStrictNullChecks.ts, 56, 6))
118+
119+
const fn4: FN = async () => {};
120+
>fn4 : Symbol(fn4, Decl(functionsMissingReturnStatementsAndExpressionsStrictNullChecks.ts, 72, 5))
121+
>FN : Symbol(FN, Decl(functionsMissingReturnStatementsAndExpressionsStrictNullChecks.ts, 56, 6))
122+

tests/baselines/reference/functionsMissingReturnStatementsAndExpressionsStrictNullChecks.types

Lines changed: 39 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -34,10 +34,10 @@ const f20: () => undefined = () => {
3434
const f21: () => undefined | number = () => {
3535
>f21 : () => undefined | number
3636
> : ^^^^^^
37-
>() => { // Error, regular void function because contextual type for implicit return isn't just undefined} : () => void
38-
> : ^^^^^^^^^^
37+
>() => { // Ok, contextual type for implicit return contains undefined} : () => undefined
38+
> : ^^^^^^^^^^^^^^^
3939

40-
// Error, regular void function because contextual type for implicit return isn't just undefined
40+
// Ok, contextual type for implicit return contains undefined
4141
}
4242

4343
const f22: () => number = () => {
@@ -132,3 +132,39 @@ f(h2);
132132
>h2 : () => undefined
133133
> : ^^^^^^
134134

135+
// https://github.com/microsoft/TypeScript/issues/57840
136+
137+
type FN = () => Promise<undefined> | undefined;
138+
>FN : FN
139+
> : ^^
140+
141+
const fn1: FN = () => {
142+
>fn1 : FN
143+
> : ^^
144+
>() => { return;} : () => undefined
145+
> : ^^^^^^^^^^^^^^^
146+
147+
return;
148+
};
149+
150+
const fn2: FN = async () => {
151+
>fn2 : FN
152+
> : ^^
153+
>async () => { return;} : () => Promise<undefined>
154+
> : ^^^^^^^^^^^^^^^^^^^^^^^^
155+
156+
return;
157+
};
158+
159+
const fn3: FN = () => {};
160+
>fn3 : FN
161+
> : ^^
162+
>() => {} : () => undefined
163+
> : ^^^^^^^^^^^^^^^
164+
165+
const fn4: FN = async () => {};
166+
>fn4 : FN
167+
> : ^^
168+
>async () => {} : () => Promise<undefined>
169+
> : ^^^^^^^^^^^^^^^^^^^^^^^^
170+

tests/baselines/reference/inferenceDoesNotAddUndefinedOrNull.types

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -46,16 +46,16 @@ function flatMapChildren<T>(node: Node, cb: (child: Node) => readonly T[] | T |
4646
> : ^^^^^^^
4747

4848
node.forEachChild(child => {
49-
>node.forEachChild(child => { const value = cb(child); if (value !== undefined) { result.push(...toArray(value)); } }) : void | undefined
50-
> : ^^^^^^^^^^^^^^^^
49+
>node.forEachChild(child => { const value = cb(child); if (value !== undefined) { result.push(...toArray(value)); } }) : undefined
50+
> : ^^^^^^^^^
5151
>node.forEachChild : <T_1>(cbNode: (node: Node) => T_1 | undefined, cbNodeArray?: (nodes: NodeArray<Node>) => T_1 | undefined) => T_1 | undefined
5252
> : ^^^^^^ ^^ ^^ ^^^ ^^^^^
5353
>node : Node
5454
> : ^^^^
5555
>forEachChild : <T_1>(cbNode: (node: Node) => T_1 | undefined, cbNodeArray?: (nodes: NodeArray<Node>) => T_1 | undefined) => T_1 | undefined
5656
> : ^^^^^^ ^^ ^^ ^^^ ^^^^^
57-
>child => { const value = cb(child); if (value !== undefined) { result.push(...toArray(value)); } } : (child: Node) => void
58-
> : ^ ^^^^^^^^^^^^^^^
57+
>child => { const value = cb(child); if (value !== undefined) { result.push(...toArray(value)); } } : (child: Node) => undefined
58+
> : ^ ^^^^^^^^^^^^^^^^^^^^
5959
>child : Node
6060
> : ^^^^
6161

@@ -118,16 +118,16 @@ function flatMapChildren2<T>(node: Node, cb: (child: Node) => readonly T[] | T |
118118
> : ^^^^^^^
119119

120120
node.forEachChild(child => {
121-
>node.forEachChild(child => { const value = cb(child); if (value !== null) { result.push(...toArray(value)); } }) : void | undefined
122-
> : ^^^^^^^^^^^^^^^^
121+
>node.forEachChild(child => { const value = cb(child); if (value !== null) { result.push(...toArray(value)); } }) : undefined
122+
> : ^^^^^^^^^
123123
>node.forEachChild : <T_1>(cbNode: (node: Node) => T_1 | undefined, cbNodeArray?: (nodes: NodeArray<Node>) => T_1 | undefined) => T_1 | undefined
124124
> : ^^^^^^ ^^ ^^ ^^^ ^^^^^
125125
>node : Node
126126
> : ^^^^
127127
>forEachChild : <T_1>(cbNode: (node: Node) => T_1 | undefined, cbNodeArray?: (nodes: NodeArray<Node>) => T_1 | undefined) => T_1 | undefined
128128
> : ^^^^^^ ^^ ^^ ^^^ ^^^^^
129-
>child => { const value = cb(child); if (value !== null) { result.push(...toArray(value)); } } : (child: Node) => void
130-
> : ^ ^^^^^^^^^^^^^^^
129+
>child => { const value = cb(child); if (value !== null) { result.push(...toArray(value)); } } : (child: Node) => undefined
130+
> : ^ ^^^^^^^^^^^^^^^^^^^^
131131
>child : Node
132132
> : ^^^^
133133

tests/cases/compiler/functionsMissingReturnStatementsAndExpressionsStrictNullChecks.ts

Lines changed: 17 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@ const f20: () => undefined = () => {
1919
}
2020

2121
const f21: () => undefined | number = () => {
22-
// Error, regular void function because contextual type for implicit return isn't just undefined
22+
// Ok, contextual type for implicit return contains undefined
2323
}
2424

2525
const f22: () => number = () => {
@@ -59,3 +59,19 @@ function h2(): undefined {
5959
}
6060

6161
f(h2);
62+
63+
// https://github.com/microsoft/TypeScript/issues/57840
64+
65+
type FN = () => Promise<undefined> | undefined;
66+
67+
const fn1: FN = () => {
68+
return;
69+
};
70+
71+
const fn2: FN = async () => {
72+
return;
73+
};
74+
75+
const fn3: FN = () => {};
76+
77+
const fn4: FN = async () => {};

0 commit comments

Comments
 (0)