Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why specify function return types?

Tags:

typescript

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.

like image 714
kevin Avatar asked Nov 17 '21 09:11

kevin


People also ask

Why do we use return type in function?

In computer programming, the return type (or result type) defines and constrains the data type of the value returned from a subroutine or method. In many programming languages (especially statically-typed programming languages such as C, C++, Java) the return type must be explicitly specified when declaring a function.

Should functions always return the same type?

So the rule that "functions should always return only one type" is rather meaningless. Having said that, there is a beautiful simplicity to functions that return objects which all share the same properties.

How do you specify the return type of a function declaration?

To define the return type for the function, we have to use the ':' symbol just after the parameter of the function and before the body of the function in TypeScript. The function body's return value should match with the function return type; otherwise, we will have a compile-time error in our code.

Can we declare a function without return type?

You may or may not use the return statement, as there is no return value. Even without the return statement, control will return to the caller automatically at the end of the function. A good utilization of a void function would be to print a header/footer to a screen or file.

How to return a value from a function in typescript?

3. : return_type: This is the standard given by the TypeScript documentation to define the return type in TypeScript. We have to use the ‘:’ colon symbol to make this function return any value from it. Immediately after this, we can specify the type that we want to return from our function; it can be anything like string, number, or any, etc.

What is a function’s return value?

These objects are known as the function’s return value. You can use them to perform further computation in your programs. Using the return statement effectively is a core skill if you want to code custom functions that are Pythonic and robust. With this knowledge, you’ll be able to write more readable, maintainable, and concise functions in Python.

What is the use of return in Python?

The Python return statement is a key component of functions and methods. You can use the return statement to make your functions send Python objects back to the caller code. These objects are known as the function’s return value. You can use them to perform further computation in your programs.

What is an example of a function returning a false value?

For example, say you need to write a function that takes two integers, a and b, and returns True if a is divisible by b. Otherwise, the function should return False.


3 Answers

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.

like image 129
jonrsharpe Avatar answered Oct 26 '22 09:10

jonrsharpe


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.

like image 44
kaya3 Avatar answered Oct 26 '22 09:10

kaya3


Here is one good reason to use explicit return types. According to 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), which reduces the amount of time spent reading and writing declaration files (e.g. for incremental builds). Type inference is very convenient, so there's no need to do this universally - however, it can be a useful thing to try if you've identified a slow section of your code.

Hence, if you don't have any problems with compliation performance I think it is not required to specify return types

Here you can find another question/answer regarding compilation performance. It also relates to TypeScript Performance wiki

P.S. you can use explicit return type for disallowing using some extra properties of return value. Consider this exmaple:

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

const result = foo()
result.age // ok
result.name //error

As you might have noticed, explicit {age:number} return type is a super type of a return value type. But it is weak, because it does not work if you want to return literal object, like here:

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

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

like image 5
captain-yossarian Avatar answered Oct 26 '22 08:10

captain-yossarian