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.
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';
}
}
}
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
}
}
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>
}
}
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"?
Most helpful comment
Just wanted to point out that this generally works without errors with the "No implicit returns" flag:
Playground link