DEV Community

Discussion on: The types in TypeScript

Collapse
 
peerreynders profile image
peerreynders • Edited

The main difference is that we don't type the return value, because is inferred.

I'm fully aware that inferred function return types are considered idiomatic in TypeScript. In the end though I think that simply reflects an attitude where convenience wins over type safety (i.e. the popularity of TypeScript + VS Code is much less about type safety but much more about convenience).


Google TypeScript Style Guide: Return types

Whether to include return type annotations for functions and methods is up to the code author. Reviewers may ask for annotations to clarify complex return types that are hard to understand. Projects may have a local policy to always require return types, but this is not a general TypeScript style requirement.

There are two benefits to explicitly typing out the implicit return values of functions and methods:

  • More precise documentation to benefit readers of the code.
  • Surface potential type errors faster in the future if there are code changes that change the return type of the function.

One minor point is the fact that functions have a type. One of the more infuriating things is that you cannot type a function declaration with a function type - one has to use arrow functions for that. So by not explicitly specifying the return type half of the function's type is left unspecified/implicit. Sure, an IDE will be able to show that type but that means that now even code reviews have to be conducted in the presence of "assistive technologies" (as the explicit code simply doesn't tell the whole story).

The major point however:

A Gentle Introduction to haskell Version 98 - 2.2.1 Recursive Types
"For example, suppose we wish to define a function fringe that returns a list of all the elements in the leaves of a tree from left to right. It's usually helpful to write down the type of new functions first"

The return type of a function is part of its design, its contract so it makes sense to explicitly determine and state it even before the body is implemented. So if one is already thinking in types and is perhaps even practicing type driven development then it makes sense to explicitly capture the types in play, perhaps even adding "more boilerplate" by adding type aliases that give the types more descriptive and meaningful names.

An implicit return type projects an attitude that the return type is merely an accidental byproduct of the function's implementation - while types are mandatory for the inputs (arguments);

// Inferred return type is ("100" | 1)
// Is that the intent?
// A union of a string literal and a numeric literal?
//
const fn = (arg: number) => arg > 50 ? '100' : 1;
Enter fullscreen mode Exit fullscreen mode

It's one thing to rely on type inference within the confines of a function body - but crossing the function (or method) boundary is significant enough to warrant an explicit type check/lint (even for one liners) in order to catch problems as close to the source as possible.

Does that mean more typing on the keyboard? Sure; not enough though to affect productivity.


Interestingly Effective Typescript has "Item 19: Avoid Cluttering Your Code with Inferable Types" (p.81) which unsurprisingly states:

"The explicit type annotation is redundant. Writing it just adds noise. If you’re unsure of the type, you can check it in your editor."

Only to later clarify (p.85):

"Similar considerations apply to a function’s return type. You may still want to annotate this even when it can be inferred to ensure that implementation errors don’t leak out into uses of the function."

The item is summarized (p.87) with:

  • Avoid writing type annotations when TypeScript can infer the same type.
  • Ideally your code has type annotations in function/method signatures but not on local variables in their bodies.
  • Consider using explicit annotations for object literals and function return types even when they can be inferred. This will help prevent implementation errors from surfacing in user code.