Business
Jobs
  • About Us
  • Solutions
    • Job Postings
      Post your job and receive qualified candidates in 48h.
    • Candidate Assessments
      500+ technical and psychological tests, plus anti-fraud.
    • Headhunting
      Tailor-made executive search from start to finish.
    • Payroll + EOR
      Payroll dispersal and EOR across 15+ LATAM countries.
  • Pricing
  • Jobs

0

264
Views
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.

over 4 years ago · Santiago Trujillo
3 answers
Answer question

0

Here's a good reason to use explicit return types. According to the TS wiki :

Adding type annotations, especially return types, can save the compiler a lot of work. In part, this is because named types tend to be more compact than anonymous types (which the compiler might infer), reducing the amount of time spent reading and writing declaration files (for example, for incremental builds). Type inference is very convenient, so there's no need to do this universally; however, it may be useful to try this if you have identified a slow section of your code.

So if you don't have any issues with build performance, I think you don't need to specify return types.

Here you can find another question/answer about build performance. Also related to TypeScript performance wiki

PS, you can use an explicit return type to disallow the use of some additional properties of the return value. Consider this example:

 const foo = (): { age: number } => { const result = { age: 42, name: 'John' } return result } const result = foo() result.age // ok result.name //error

As you may have noticed, the explicit return type {age:number} is a supertype of a return value type. But it's weak, because it doesn't work if you want to return an object literal, like here:

 const foo = (): { age: number } => ({ age: 42, name: 'John' // error })

So I can't recommend using this technique, however it is worth knowing.

over 4 years ago · Santiago Trujillo Report

0

The main reason to annotate the return type of a function is the same as the main reason to use Typescript at all: because you want the compiler to check your code and give you useful error messages when you make certain kinds of mistakes.

If you let Typescript infer the return type of your function, then whatever it infers will be the return type of the function, even if the inferred type is not the type you intended. In this case, if you try to return something of the wrong type, the compiler will use that wrong type as the function's return type and you will get an error when you try to call the function and expect something of the right type - or worse, you won't get an error, but your code will fail at runtime or do the wrong thing.

On the other hand, if you know what you want the return type to be, and you want the compiler to check that your function actually does return something of that type, then you should use a type annotation. In this case, if you try to return something of the wrong type, then you'll get an error in the function itself, where the mistake actually was.

over 4 years ago · Santiago Trujillo Report

0

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 angular 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.

over 4 years ago · Santiago Trujillo Report
Answer question
Find remote jobs

Discover the new way to find a job!

Top jobs
Top job categories
Business
Post vacancy Pricing Sales
Legal
Terms and conditions Privacy policy
© 2026 PeakU Inc. All Rights Reserved.
Andres GPT
Show me some job opportunities
There's an error!