Skip to content

Don't error when function has an implicit return but its return type is assignable to undefined #53490

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 10 commits into from
Mar 27, 2023
Merged
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion src/compiler/checker.ts
Original file line number Diff line number Diff line change
Expand Up @@ -35686,7 +35686,7 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker {
error(errorNode, Diagnostics.A_function_whose_declared_type_is_neither_void_nor_any_must_return_a_value);
}
}
else if (type && strictNullChecks) {
else if (type && strictNullChecks && !isTypeAssignableTo(undefinedType, type)) {
error(errorNode, Diagnostics.Function_lacks_ending_return_statement_and_return_type_does_not_include_undefined);
}
else if (compilerOptions.noImplicitReturns) {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
//// [functionsWithImplicitReturnTypeAssignableToUndefined.ts]
function f1(): {} {
if (Math.random() < 0.5) return {};

// Implicit return, but undefined is assignable to object.
}

function f2(): Record<string, any> {
if (Math.random() < 0.5) return { "foo": true };

// Implicit return, but undefined is assignable to records (which are just fancy objects).
}

function f3(): null {
if (Math.random() < 0.5) return null;

// Implicit return, but undefined is assignable to null.
}

function f4(): string | null {
if (Math.random() < 0.5) return "string";

// Implicit return, but undefined is assignable to null.
}


//// [functionsWithImplicitReturnTypeAssignableToUndefined.js]
function f1() {
if (Math.random() < 0.5)
return {};
// Implicit return, but undefined is assignable to object.
}
function f2() {
if (Math.random() < 0.5)
return { "foo": true };
// Implicit return, but undefined is assignable to records (which are just fancy objects).
}
function f3() {
if (Math.random() < 0.5)
return null;
// Implicit return, but undefined is assignable to null.
}
function f4() {
if (Math.random() < 0.5)
return "string";
// Implicit return, but undefined is assignable to null.
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
=== tests/cases/compiler/functionsWithImplicitReturnTypeAssignableToUndefined.ts ===
function f1(): {} {
>f1 : Symbol(f1, Decl(functionsWithImplicitReturnTypeAssignableToUndefined.ts, 0, 0))

if (Math.random() < 0.5) return {};
>Math.random : Symbol(Math.random, Decl(lib.es5.d.ts, --, --))
>Math : Symbol(Math, Decl(lib.es5.d.ts, --, --), Decl(lib.es5.d.ts, --, --))
>random : Symbol(Math.random, Decl(lib.es5.d.ts, --, --))

// Implicit return, but undefined is assignable to object.
}

function f2(): Record<string, any> {
>f2 : Symbol(f2, Decl(functionsWithImplicitReturnTypeAssignableToUndefined.ts, 4, 1))
>Record : Symbol(Record, Decl(lib.es5.d.ts, --, --))

if (Math.random() < 0.5) return { "foo": true };
>Math.random : Symbol(Math.random, Decl(lib.es5.d.ts, --, --))
>Math : Symbol(Math, Decl(lib.es5.d.ts, --, --), Decl(lib.es5.d.ts, --, --))
>random : Symbol(Math.random, Decl(lib.es5.d.ts, --, --))
>"foo" : Symbol("foo", Decl(functionsWithImplicitReturnTypeAssignableToUndefined.ts, 7, 37))

// Implicit return, but undefined is assignable to records (which are just fancy objects).
}

function f3(): null {
>f3 : Symbol(f3, Decl(functionsWithImplicitReturnTypeAssignableToUndefined.ts, 10, 1))

if (Math.random() < 0.5) return null;
>Math.random : Symbol(Math.random, Decl(lib.es5.d.ts, --, --))
>Math : Symbol(Math, Decl(lib.es5.d.ts, --, --), Decl(lib.es5.d.ts, --, --))
>random : Symbol(Math.random, Decl(lib.es5.d.ts, --, --))

// Implicit return, but undefined is assignable to null.
}

function f4(): string | null {
>f4 : Symbol(f4, Decl(functionsWithImplicitReturnTypeAssignableToUndefined.ts, 16, 1))

if (Math.random() < 0.5) return "string";
>Math.random : Symbol(Math.random, Decl(lib.es5.d.ts, --, --))
>Math : Symbol(Math, Decl(lib.es5.d.ts, --, --), Decl(lib.es5.d.ts, --, --))
>random : Symbol(Math.random, Decl(lib.es5.d.ts, --, --))

// Implicit return, but undefined is assignable to null.
}

Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
=== tests/cases/compiler/functionsWithImplicitReturnTypeAssignableToUndefined.ts ===
function f1(): {} {
>f1 : () => {}

if (Math.random() < 0.5) return {};
>Math.random() < 0.5 : boolean
>Math.random() : number
>Math.random : () => number
>Math : Math
>random : () => number
>0.5 : 0.5
>{} : {}

// Implicit return, but undefined is assignable to object.
}

function f2(): Record<string, any> {
>f2 : () => Record<string, any>

if (Math.random() < 0.5) return { "foo": true };
>Math.random() < 0.5 : boolean
>Math.random() : number
>Math.random : () => number
>Math : Math
>random : () => number
>0.5 : 0.5
>{ "foo": true } : { foo: boolean; }
>"foo" : boolean
>true : true

// Implicit return, but undefined is assignable to records (which are just fancy objects).
}

function f3(): null {
>f3 : () => null

if (Math.random() < 0.5) return null;
>Math.random() < 0.5 : boolean
>Math.random() : number
>Math.random : () => number
>Math : Math
>random : () => number
>0.5 : 0.5

// Implicit return, but undefined is assignable to null.
}

function f4(): string | null {
>f4 : () => string | null

if (Math.random() < 0.5) return "string";
>Math.random() < 0.5 : boolean
>Math.random() : number
>Math.random : () => number
>Math : Math
>random : () => number
>0.5 : 0.5
>"string" : "string"

// Implicit return, but undefined is assignable to null.
}

Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
//// [functionsWithImplicitReturnTypeAssignableToUndefinedStrictNullChecks.ts]
function f1(): unknown {
if (Math.random() < 0.5) return true;

// Implicit return, but undefined is assignable to unknown.
}

function f2(): any {
// Implicit return, but undefined is assignable to any.
}

function f3(): void {
// Implicit return, but undefined is assignable to void.
}

//// [functionsWithImplicitReturnTypeAssignableToUndefinedStrictNullChecks.js]
function f1() {
if (Math.random() < 0.5)
return true;
// Implicit return, but undefined is assignable to unknown.
}
function f2() {
// Implicit return, but undefined is assignable to any.
}
function f3() {
// Implicit return, but undefined is assignable to void.
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
=== tests/cases/compiler/functionsWithImplicitReturnTypeAssignableToUndefinedStrictNullChecks.ts ===
function f1(): unknown {
>f1 : Symbol(f1, Decl(functionsWithImplicitReturnTypeAssignableToUndefinedStrictNullChecks.ts, 0, 0))

if (Math.random() < 0.5) return true;
>Math.random : Symbol(Math.random, Decl(lib.es5.d.ts, --, --))
>Math : Symbol(Math, Decl(lib.es5.d.ts, --, --), Decl(lib.es5.d.ts, --, --))
>random : Symbol(Math.random, Decl(lib.es5.d.ts, --, --))

// Implicit return, but undefined is assignable to unknown.
}

function f2(): any {
>f2 : Symbol(f2, Decl(functionsWithImplicitReturnTypeAssignableToUndefinedStrictNullChecks.ts, 4, 1))

// Implicit return, but undefined is assignable to any.
}

function f3(): void {
>f3 : Symbol(f3, Decl(functionsWithImplicitReturnTypeAssignableToUndefinedStrictNullChecks.ts, 8, 1))

// Implicit return, but undefined is assignable to void.
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
=== tests/cases/compiler/functionsWithImplicitReturnTypeAssignableToUndefinedStrictNullChecks.ts ===
function f1(): unknown {
>f1 : () => unknown

if (Math.random() < 0.5) return true;
>Math.random() < 0.5 : boolean
>Math.random() : number
>Math.random : () => number
>Math : Math
>random : () => number
>0.5 : 0.5
>true : true

// Implicit return, but undefined is assignable to unknown.
}

function f2(): any {
>f2 : () => any

// Implicit return, but undefined is assignable to any.
}

function f3(): void {
>f3 : () => void

// Implicit return, but undefined is assignable to void.
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
// @noImplicitReturns: false
// @strictNullChecks: false

function f1(): {} {
if (Math.random() < 0.5) return {};

// Implicit return, but undefined is assignable to object.
}

function f2(): Record<string, any> {
if (Math.random() < 0.5) return { "foo": true };

// Implicit return, but undefined is assignable to records (which are just fancy objects).
}

function f3(): null {
if (Math.random() < 0.5) return null;

// Implicit return, but undefined is assignable to null.
}

function f4(): string | null {
if (Math.random() < 0.5) return "string";

// Implicit return, but undefined is assignable to null.
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
// @noImplicitReturns: false
// @strictNullChecks: true

function f1(): unknown {
if (Math.random() < 0.5) return true;

// Implicit return, but undefined is assignable to unknown.
}

function f2(): any {
// Implicit return, but undefined is assignable to any.
}

function f3(): void {
// Implicit return, but undefined is assignable to void.
}