How to Specify Return Type in TypeScript Arrow Functions?

How to specify return type in TypeScript arrow function?

I am using React and Redux with action types defined as interfaces, which allows my reducers to take advantage of tagged union types for better type safety. I have the following type declarations:

interface AddTodoAction { type: “ADD_TODO”, text: string };

interface DeleteTodoAction { type: “DELETE_TODO”, id: number }

type TodoAction = AddTodoAction | DeleteTodoAction;

I want to create helper functions that generate these actions, and I prefer using arrow functions for this. For instance, I wrote the following:

export const addTodo1 = (text: string) => ({ type: “ADD_TODO”, text });

However, the compiler doesn’t provide any assistance in ensuring that this is a valid AddTodoAction because the return type isn’t specified. I can explicitly specify the return type like this:

export const addTodo2: (text: string) => AddTodoAction = (text: string) => ({ type: “ADD_TODO”, text });

But this approach requires specifying the function arguments twice, which feels verbose and less readable.

I considered trying this:

export const addTodo3 = (text: string) => ({ type: “ADD_TODO”, text });

In this case, TypeScript infers the return type as AddTodoAction, but it doesn’t validate that the returned object has all the correct fields.

I could solve this by switching to a different function syntax:

export const addTodo4 = function(text: string): AddTodoAction { return { type: “ADD_TODO”, text }; }

export function addTodo5(text: string): AddTodoAction { return { type: “ADD_TODO”, text }; } These alternatives make the compiler use the correct return type and enforce that I include all required fields, but they are more verbose and also change the way this is handled in the function (which might not be a problem).

Is there any advice or best practice for specifying the return type in typescript arrow function return type while keeping it concise and readable?

From my experience, if you’re looking to simplify things while keeping it clean and maintainable, using TypeScript’s ReturnType utility is a great choice. It ensures type safety without repeating yourself:

export const addTodo1 = (text: string) => ({
    type: "ADD_TODO",
    text
});

// Use ReturnType to infer the return type automatically
export const addTodo2 = (text: string): ReturnType<typeof addTodo1> => ({
    type: "ADD_TODO",
    text
});

This approach ensures typescript arrow function return type validation without being verbose. It’s concise, and TypeScript takes care of the heavy lifting for you.

I completely agree with Tim’s point, but let me add another option I’ve found handy when working with typescript arrow function return type. You can use type assertions within the arrow function to explicitly cast the return type, which keeps the code clean without having to rely on ReturnType:

export const addTodo3 = (text: string) => ({
    type: "ADD_TODO",
    text
}) as AddTodoAction;

This way, you’re telling TypeScript exactly what the return type should be. However, keep in mind that type assertions bypass some checks, so it’s a bit like saying ‘I know what I’m doing.’ Use it responsibly!

I think both of those solutions are great, but if you want a middle ground that still lets TypeScript infer precise types without extra effort, consider using as const. It’s particularly effective for typescript arrow function return type when working with fixed, literal values:

export const addTodo4 = (text: string) => ({
    type: "ADD_TODO",
    text
} as const);

// Or combine with explicit return type for even stronger safety
export const addTodo5 = (text: string): AddTodoAction => ({
    type: "ADD_TODO",
    text
});

Using as const locks in the specific literal type for type: "ADD_TODO", ensuring TypeScript knows exactly what to expect. It’s a small addition, but it makes your code more robust.