Follow

Keep Up to Date with the Most Important News

By pressing the Subscribe button, you confirm that you have read and are agreeing to our Privacy Policy and Terms of Use
Contact

Why specify function return types?

Most of the time I read that we should use type inference as much as possible. When writing a function I understand we must type arguments since they cannot be inferred, but why do we have to type the return value? TypeScript is taking care of that. What are the benefits of explicitly typing the return value of a function? So far I only read that I should do it, but no one is saying why.

>Solution :

MEDevel.com: Open-source for Healthcare and Education

Collecting and validating open-source software for healthcare, education, enterprise, development, medical imaging, medical records, and digital pathology.

Visit Medevel

The compiler can infer what your code does, but it has no idea what you intended. Take this trivial example:

function thing(value: string) {
    return value === "foo" ? 123 : "456";
}

The inferred type is function thing(value: string): 123 | "456", and that matches the implementation, but is that in any meaningful sense right? Perhaps I intended to always return a number, for example; if I tell the compiler that, it can tell me I didn’t:

Type 'string | number' is not assignable to type 'number'.
  Type 'string' is not assignable to type 'number'.(2322)

Particularly when you’re using complex wrapper/generic types (e.g. I see issues around this a lot in where RxJS observables are being used), this can really help getting early feedback on your assumptions.


I also work a lot with test-driven development (TDD), where one of the values of writing tests before implementation is that it gives you the opportunity to discuss the interface at a time where the cost of changing it is close to zero. Using the classic RPS example:

function rps(left: string, right: string) {
  return "right";
}

it("returns 'right' for 'rock' vs. 'paper'", () => {
  expect(rps("rock", "paper")).to.equal("right");
});

That’ll compile and pass, but is it what we want? Now we can talk about options:

  • Do we accept the inferred type function rps(left: string, right: string): string?

  • Go more specific with e.g.

    type Throw = "rock" | "paper" | "scissors";
    type Outcome = "left" | "right" : "draw";
    function rps(left: Throw, right: Throw): Outcome { ... }
    
  • Use enums instead?

We can talk through the trade-offs and pick the best option given what we know at the time. The explicit return type serves as documentation of the decision we made.


I’d recommend turning on @typescript-eslint/explicit-function-return-type if you’re linting your code, which provides as its rationale:

Explicit types for function return values makes it clear to any
calling code what type is returned. This ensures that the return value
is assigned to a variable of the correct type; or in the case where
there is no return value, that the calling code doesn’t try to use the
undefined value when it shouldn’t.

Add a comment

Leave a Reply

Keep Up to Date with the Most Important News

By pressing the Subscribe button, you confirm that you have read and are agreeing to our Privacy Policy and Terms of Use

Discover more from Dev solutions

Subscribe now to keep reading and get access to the full archive.

Continue reading