Skip to content

Exhaustive checking of overloaded function return types #13225

@rotemdan

Description

@rotemdan

Currently:

function func(a: number): number; function func(a: string): string; function func(a: number | string): number | string { return 54; } const result = func("hi"); // result gets type `string`, however the  // actual return type would always be `number`  // (or more specifically, the literal type `54`).

And:

function func(a: number): number; function func(a: string): string; function func(a: number | string): number | string { if (typeof a === "number") return "oops"; // this doesn't error else if (typeof a === "string") return 54; // nor this }

There are several stages for gradually addressing this:

  1. Check there exists at least one code path that returns each possible return type (either a string or a number in this example) or a type that's a assignable to the union of all possible return types (returning a value with type string | number might work as well, although it wouldn't be really 'safe') such that the union of all actual return types at all return paths exactly matches the union of all expected return types (I might give an example to clarify what this means later).
  2. Check that code paths where a is narrowed to number always return a number and when narrowed to string, always return a string (note that this check would only be reliable if the parameter a is not itself reassigned with a value having an incompatible type before the runtime assertion. Unfortunately there is no current way to modify function parameters with the equivalent of const or readonly so it has to be determined based on flow analysis).
  3. In the body of the function, infer the return type as a guarded polymorphic type that describes the relationship between parameter and return types (and possibly infer similar types to describe relationships between the parameters themselves) and even allow to defer the resolution of the return type back to the caller when the parameter given to func is itself a union (I'm not sure if that's currently possible, but it could be useful, see more details about how this can be achieved in the linked comment).

Metadata

Metadata

Assignees

No one assigned

    Labels

    Working as IntendedThe behavior described is the intended behavior; this is not a bug

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions