DEV Community

Cover image for Raise The Red Flag Early: The Power of Explicit Return Types in TypeScript
Schalk Neethling
Schalk Neethling

Posted on • Originally published at schalkneethling.com

Raise The Red Flag Early: The Power of Explicit Return Types in TypeScript

As developers, we're always on the lookout for ways to prevent errors before they make their way into production. One of the ways TypeScript can be your friend here is through the use of explicit return type annotations. This seemingly simple feature can dramatically improve your code's reliability and provide a better developer experience, especially for consumers of an API, utility functions, or library.

The Subtle Pitfall of Implicit Returns

Consider a function that looks innocent enough:

function getUserStatus(lastLogin: Date) {
  const daysSinceLogin =
    (Date.now() - lastLogin.getTime()) / (1000 * 60 * 60 * 24);

  if (daysSinceLogin < 7) {
    return "active";
  }

  if (daysSinceLogin < 30) {
    return "inactive";
  }
}
Enter fullscreen mode Exit fullscreen mode

At first glance, this function seems fine. But there's a hidden problem: if the conditions aren't met, the function will implicitly return undefined. This can lead to runtime errors that may be difficult to track down. Also, instead of TypeScript raising an error right at the function definition, the error would only be raised at the call site.

You can understand how frustrating it can be for developers using your code. So how would explicit return types help here?

The Magic of Explicit Return Types

By adding an explicit return type, TypeScript will raise the red flag immediately at the function definition:

function getUserStatus(lastLogin: Date): "active" | "inactive" | "dormant" {
  const daysSinceLogin =
    (Date.now() - lastLogin.getTime()) / (1000 * 60 * 60 * 24);

  if (daysSinceLogin < 7) {
    return "active";
  }

  if (daysSinceLogin < 30) {
    return "inactive";
  }
}
Enter fullscreen mode Exit fullscreen mode

If you enter this code into the TypeScript Playground, you will immediately be presented with an error:

Function lacks ending return statement and return type does not include 'undefined'.

You as the author of this function can now be proactive in addressing this before it becomes a problem.

function getUserStatus(lastLogin: Date): "active" | "inactive" | "dormant" {
  const daysSinceLogin =
    (Date.now() - lastLogin.getTime()) / (1000 * 60 * 60 * 24);

  if (daysSinceLogin < 7) {
    return "active";
  }

  if (daysSinceLogin < 30) {
    return "inactive";
  }

  return "dormant";
}
Enter fullscreen mode Exit fullscreen mode

Why This Matters

The benefits are immediate and powerful:

  1. Early Error Detection: TypeScript will highlight the issue at the function definition, not at the call site.

  2. Forced Completeness: By specifying a return type, you're essentially creating a contract that your function must fulfill. TypeScript becomes your code reviewer, ensuring you handle all possible scenarios.

  3. Improved Code Readability: Explicit return types serve as documentation. They clearly communicate what a function is expected to return.

Update: 11 December 2024

A reader (Pierre Spring) reminded me on Mastodon that you can configure TypeScript to prevent you from not defining a return. You can do this by setting the noImplicitReturns flag to true in your tsconfig.json file.

{
  "compilerOptions": {
    "noImplicitReturns": true
  }
}
Enter fullscreen mode Exit fullscreen mode

Learn more about this flag in the TypeScript documentation.

Credit Where It's Due

This approach was inspired by a lesson from Mike North's TypeScript course on Frontend Masters. It's a testament to how small, intentional typing choices can significantly improve code quality and usability.

Conclusion

Defining explicit return types is more than just a TypeScript feature; it encourages you to think through all possible code paths, make your intentions clear, and catch potential issues before they become pain points. I hope you found this helpful and that you will use this to make your functions more resilient. Happy coding! 🚀

Top comments (0)