Skip to content
Merged
Show file tree
Hide file tree
Changes from 6 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
3 changes: 2 additions & 1 deletion src/compiler/checker.ts
Original file line number Diff line number Diff line change
Expand Up @@ -35660,7 +35660,8 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker {
const type = returnType && unwrapReturnType(returnType, functionFlags);

// Functions with an explicitly specified 'undefined, 'void' or 'any' return type don't need any return expressions.
if (type && maybeTypeOfKind(type, TypeFlags.Undefined | TypeFlags.Void | TypeFlags.Any)) {
// 'unknown' is also fine because it is always assignable to 'undefined'.
Comment thread
MariaSolOs marked this conversation as resolved.
Outdated
if (type && maybeTypeOfKind(type, TypeFlags.Undefined | TypeFlags.Void | TypeFlags.Any | TypeFlags.Unknown)) {
return;
}

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

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

type MyUnknown = unknown;
function f2(): unknown {
if (Math.random() < 0.5) return true;

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

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

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

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

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

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

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

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

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

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

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

//// [functionsWithImplicitReturnTypeAssignableToUndefined.js]
function f1() {
if (Math.random() < 0.5)
return true;
// Implicit return, but undefined is always assignable to unknown.
}
function f2() {
if (Math.random() < 0.5)
return true;
// Implicit return, but undefined is always assignable to unknown.
}
function f3() {
// Implicit return, but undefined is always assignable to any.
}
function f4() {
// Implicit return, but undefined is always assignable to void.
}
function f5() {
if (Math.random() < 0.5)
return {};
// Implicit return, but undefined is assignable to object when strictNullChecks is off.
}
function f6() {
if (Math.random() < 0.5)
return { "foo": true };
// Implicit return, but undefined is assignable to records (which are just fancy objects)
// when strictNullChecks is off.
}
function f7() {
if (Math.random() < 0.5)
return null;
// Implicit return, but undefined is assignable to null when strictNullChecks is off.
}
function f8() {
if (Math.random() < 0.5)
return "foo";
// Implicit return, but undefined is assignable to null when strictNullChecks is off.
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
=== tests/cases/compiler/functionsWithImplicitReturnTypeAssignableToUndefined.ts ===
function f1(): unknown {
>f1 : Symbol(f1, Decl(functionsWithImplicitReturnTypeAssignableToUndefined.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 always assignable to unknown.
}

type MyUnknown = unknown;
>MyUnknown : Symbol(MyUnknown, Decl(functionsWithImplicitReturnTypeAssignableToUndefined.ts, 4, 1))

function f2(): unknown {
>f2 : Symbol(f2, Decl(functionsWithImplicitReturnTypeAssignableToUndefined.ts, 6, 25))

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 always assignable to unknown.
}

function f3(): any {
>f3 : Symbol(f3, Decl(functionsWithImplicitReturnTypeAssignableToUndefined.ts, 11, 1))

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

function f4(): void {
>f4 : Symbol(f4, Decl(functionsWithImplicitReturnTypeAssignableToUndefined.ts, 15, 1))

// Implicit return, but undefined is always assignable to void.
}

function f5(): {} {
>f5 : Symbol(f5, Decl(functionsWithImplicitReturnTypeAssignableToUndefined.ts, 19, 1))

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 when strictNullChecks is off.
}

function f6(): Record<string, any> {
>f6 : Symbol(f6, Decl(functionsWithImplicitReturnTypeAssignableToUndefined.ts, 25, 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, 28, 37))

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

function f7(): null {
>f7 : Symbol(f7, Decl(functionsWithImplicitReturnTypeAssignableToUndefined.ts, 32, 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 when strictNullChecks is off.
}

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

if (Math.random() < 0.5) return "foo";
>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 when strictNullChecks is off.
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,107 @@
=== tests/cases/compiler/functionsWithImplicitReturnTypeAssignableToUndefined.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 always assignable to unknown.
}

type MyUnknown = unknown;
>MyUnknown : unknown

function f2(): unknown {
>f2 : () => 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 always assignable to unknown.
}

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

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

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

// Implicit return, but undefined is always assignable to void.
}

function f5(): {} {
>f5 : () => {}

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 when strictNullChecks is off.
}

function f6(): Record<string, any> {
>f6 : () => 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)
// when strictNullChecks is off.
}

function f7(): null {
>f7 : () => 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 when strictNullChecks is off.
}

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

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

// Implicit return, but undefined is assignable to null when strictNullChecks is off.
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
tests/cases/compiler/functionsWithImplicitReturnTypeAssignableToUndefined.ts(22,16): error TS2366: Function lacks ending return statement and return type does not include 'undefined'.
tests/cases/compiler/functionsWithImplicitReturnTypeAssignableToUndefined.ts(28,16): error TS2366: Function lacks ending return statement and return type does not include 'undefined'.
tests/cases/compiler/functionsWithImplicitReturnTypeAssignableToUndefined.ts(35,16): error TS2366: Function lacks ending return statement and return type does not include 'undefined'.
tests/cases/compiler/functionsWithImplicitReturnTypeAssignableToUndefined.ts(41,16): error TS2366: Function lacks ending return statement and return type does not include 'undefined'.


==== tests/cases/compiler/functionsWithImplicitReturnTypeAssignableToUndefined.ts (4 errors) ====
function f1(): unknown {
if (Math.random() < 0.5) return true;

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

type MyUnknown = unknown;
function f2(): unknown {
if (Math.random() < 0.5) return true;

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

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

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

function f5(): {} {
~~
!!! error TS2366: Function lacks ending return statement and return type does not include 'undefined'.
if (Math.random() < 0.5) return {};

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

function f6(): Record<string, any> {
~~~~~~~~~~~~~~~~~~~
!!! error TS2366: Function lacks ending return statement and return type does not include 'undefined'.
if (Math.random() < 0.5) return { "foo": true };

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

function f7(): null {
~~~~
!!! error TS2366: Function lacks ending return statement and return type does not include 'undefined'.
if (Math.random() < 0.5) return null;

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

function f8(): string | null {
~~~~~~~~~~~~~
!!! error TS2366: Function lacks ending return statement and return type does not include 'undefined'.
if (Math.random() < 0.5) return "foo";

// Implicit return, but undefined is assignable to null when strictNullChecks is off.
}
Loading