Typescript: Surprising `Not all code paths return a value`

Created on 7 Sep 2017  路  6Comments  路  Source: microsoft/TypeScript

TypeScript Version: 2.5.2

Code

type AsyncFunction<T> = () => Promise<T>;

const f: AsyncFunction<number | void> = async () => {
  if (Math.random()) {
    return 1;
  }
};

Expected behavior:

No tsc errors.

Actual behavior:

const f: AsyncFunction<number | void> = async () => {
//                                      ~~~~~~~~~~~~~ [ts] Not all code paths return a value.

  if (Math.random()) {
    return 1;
  }
};

Notably, when using return-type annotation, this error does not occur:

async function a(): Promise<number | void> {
  if (Math.random()) {
    return 1;
  }
}

const b = async (): Promise<number | void> => {
  if (Math.random()) {
    return 1;
  }
};

In our real-world scenario, it would not be sufficient to look for this syntactic pattern. Rather, the fact that the async function is assigned to a union containing void should be the trigger that silences this error.

Suggestion help wanted

Most helpful comment

Just wanted to point out that this generally works without errors with the "No implicit returns" flag:

class Foo {
  maybeReturn(): string | void {
    if (Math.random() > 0.5) {
      return 'hello';
    }
  }
}

Playground link

All 6 comments

Doesn't require async

type fn = () => (void | number);

var x: fn = () => {
  if (Math.random()) {
    return 1;
  }
}

I'm not really sure if this error is "unexpected" or not. It's controlled by the "No implicit returns" flag and this is definitely an implicit return.

Just wanted to point out that this generally works without errors with the "No implicit returns" flag:

class Foo {
  maybeReturn(): string | void {
    if (Math.random() > 0.5) {
      return 'hello';
    }
  }
}

Playground link

PRs welcome

Related bug in v3.7.2. noImplicitReturns doesn't affect the behavior.

// Error TS2366: Function lacks ending return statement and return
// type does not include 'undefined'.
function f(x: number): number {
  if (typeof x === 'number') {
    return 1
  }
}

Playground Link

Notably, this prevents you from expressing React props as unions:


import * as React from 'react'

type Props =
   | { a: number }
   | { b: string }

// Error TS2366: Function lacks ending return statement and return
// type does not include 'undefined'.
function MyComponent(props: Props): JSX.Element {
  if ('a' in props) {
    return <div>a is {props.a}</div>
  }
  if ('b' in props) {
    return <div>b is {props.b}</div>
  }
}

Playground Link

For this example to type check, we need to add a catch-all return statement at the end of MyComponent's body, which is not actually needed since at that point, props has already been refined to never.

Not sure if related to this but I get the same behavior when returning void:

const aa = (a: any) => { // No error
  if (a) return
}

const bb = (b: any) => { // Not all code paths return a value.
  if (b) return void console.log("foo")
}

return void should be considered as a path that does not return a value, right?

@mhegazy since some time has gone by: is this still "PRs welcome"?

Was this page helpful?
0 / 5 - 0 ratings

Related issues

jbondc picture jbondc  路  3Comments

zhuravlikjb picture zhuravlikjb  路  3Comments

Roam-Cooper picture Roam-Cooper  路  3Comments

MartynasZilinskas picture MartynasZilinskas  路  3Comments

blendsdk picture blendsdk  路  3Comments