How to fix Function expression requires a return type in TypeScript

TypeScriptBEGINNERMEDIUMUpdated Dec 22, 2025ยท 162 views

This ESLint error occurs when the @typescript-eslint/explicit-function-return-type rule is enabled and you define a function expression without explicitly annotating its return type. While TypeScript can infer return types, this linting rule enforces explicit annotations for better code clarity and type safety.

Quick fix

Fixes Function expression requires a return type

// BEFORE: ESLint error - no return type annotation
const greet = (name: string) => {
  return `Hello, ${name}`;
};

// AFTER: Explicit return type added
const greet = (name: string): string => {
  return `Hello, ${name}`;
};
  1. Add explicit return type annotation to function expressions
  2. Use typed function expressions with interface or type
  3. Configure ESLint to allow expressions without return types
  4. Disable the rule entirely (not recommended)
typescript โ€” error
// BEFORE: ESLint error - no return type annotation
const greet = (name: string) => {
return `Hello, ${name}`;
};
// AFTER: Explicit return type added
const greet = (name: string): string => {
return `Hello, ${name}`;
};
Function expression requires a return type

What this error means

This error is triggered by ESLint, not the TypeScript compiler itself. Specifically, it comes from the `@typescript-eslint/explicit-function-return-type` rule, which enforces explicit return type annotations on functions and function expressions. TypeScript's type inference is generally powerful enough to determine function return types automatically. However, many teams enable this ESLint rule because explicit return types: 1. **Make code more self-documenting**: Readers immediately know what a function returns without inspecting the implementation 2. **Prevent accidental type changes**: If you modify a function's logic and accidentally change the return type, the compiler will catch it 3. **Improve IDE autocomplete**: Explicit types help IDEs provide better suggestions 4. **Ensure consistent codebase style**: All functions follow the same annotation pattern The rule primarily targets function expressions (arrow functions, function expressions assigned to variables) but can be configured to also check function declarations.

Typical symptoms

  • ESLint error shown in your IDE with message 'Function expression requires a return type'
  • Build fails with ESLint errors when using strict linting configurations
  • Error appears on arrow functions assigned to variables or constants
  • Error appears on callback functions passed as arguments
  • TypeScript compiler succeeds but ESLint validation fails
  • CI/CD pipeline fails during lint step despite code working correctly

Common causes

  1. ESLint rule @typescript-eslint/explicit-function-return-type is enabled in your configuration
  2. Function expression (arrow function or function assigned to variable) lacks explicit return type annotation
  3. Strict TypeScript ESLint preset is active (recommended, recommended-type-checked, strict)
  4. Team coding standards require explicit return types for all functions
  5. Higher-order functions (functions returning functions) without proper type annotations
  6. Callback functions passed to methods like map, filter, reduce without return type

How to fix "Function expression requires a return type"

1Add explicit return type annotation to function expressions

The most straightforward fix is to add a return type annotation after the parameter list:

typescript
// BEFORE: ESLint error - no return type annotation
const greet = (name: string) => {
  return `Hello, ${name}`;
};

// AFTER: Explicit return type added
const greet = (name: string): string => {
  return `Hello, ${name}`;
};

For function expressions with multiple possible return types:

typescript
// Union return type
const getValue = (useDefault: boolean): string | number => {
  return useDefault ? "default" : 42;
};

// Void for functions with no return value
const logMessage = (msg: string): void => {
  console.log(msg);
};

// Promise return type for async functions
const fetchData = async (id: string): Promise<User> => {
  const response = await fetch(`/api/users/${id}`);
  return response.json();
};
2Use typed function expressions with interface or type

If you define a function type separately, the rule allows you to skip the inline annotation (when allowTypedFunctionExpressions is enabled, which is default):

typescript
// Define function type
type GreetFunction = (name: string) => string;

// BEFORE: Error - function expression needs return type
const greet = (name: string) => {
  return `Hello, ${name}`;
};

// AFTER: Variable has function type annotation
const greet: GreetFunction = (name) => {
  return `Hello, ${name}`;
};

This is especially useful for callback patterns:

typescript
// React event handler example
type ClickHandler = (event: React.MouseEvent<HTMLButtonElement>) => void;

const handleClick: ClickHandler = (event) => {
  event.preventDefault();
  console.log("Clicked!");
};
3Configure ESLint to allow expressions without return types

If you want to keep type inference for function expressions, update your .eslintrc.js or .eslintrc.json:

json
{
  "rules": {
    "@typescript-eslint/explicit-function-return-type": [
      "error",
      {
        "allowExpressions": true,
        "allowTypedFunctionExpressions": true,
        "allowHigherOrderFunctions": true
      }
    ]
  }
}

Configuration options:

- allowExpressions: true - Ignores function expressions (arrow functions, function expressions)
- allowTypedFunctionExpressions: true - Allows functions with type annotations on the variable
- allowHigherOrderFunctions: true - Allows functions that return other functions
- allowDirectConstAssertionInArrowFunctions: true - Allows as const assertions
- allowConciseArrowFunctionExpressionsStartingWithVoid: true - Allows () => void expr

This gives you flexibility while maintaining type safety where it matters most.

4Disable the rule entirely (not recommended)

If your team prefers to rely on TypeScript's type inference, you can disable the rule:

json
{
  "rules": {
    "@typescript-eslint/explicit-function-return-type": "off"
  }
}

Or use inline comments to disable it for specific functions:

typescript
// eslint-disable-next-line @typescript-eslint/explicit-function-return-type
const calculateTotal = (items: Item[]) => {
  return items.reduce((sum, item) => sum + item.price, 0);
};

Note: Disabling this rule sacrifices some code clarity and type safety benefits. Consider using the configuration options in Step 3 instead for better balance.

5Use explicit return types for exported functions

Even if you disable the rule for internal functions, consider keeping explicit return types for public APIs and exported functions:

typescript
// Public API - explicit return type recommended
export const processUser = (user: User): ProcessedUser => {
  return {
    id: user.id,
    displayName: `${user.firstName} ${user.lastName}`,
    isActive: user.status === "active",
  };
};

// Internal helper - type inference OK
const formatName = (first: string, last: string) => {
  return `${first} ${last}`;
};

This can be enforced with the @typescript-eslint/explicit-module-boundary-types rule instead, which only checks exported functions.

Advanced notes

### Balancing Type Safety and Developer Experience

Many teams debate whether to enforce explicit return types. The trade-offs:

Pros of explicit return types:
- Acts as inline documentation
- Catches refactoring mistakes early
- Prevents accidental any returns
- Makes code reviews easier

Pros of type inference:
- Less verbose code
- TypeScript's inference is very accurate
- Faster development for simple functions
- Reduces maintenance burden when types change

Recommended approach: Use the explicit-module-boundary-types rule instead, which only requires return types on exported functions. This balances internal flexibility with external type safety.

### Higher-Order Functions

Functions that return functions can be tricky:

typescript
// Without explicit types - may trigger error
const createMultiplier = (factor: number) => {
  return (value: number) => value * factor;
};

// With explicit types - clear and error-free
const createMultiplier = (factor: number): ((value: number) => number) => {
  return (value: number): number => value * factor;
};

// Or use a type alias for clarity
type Multiplier = (value: number) => number;
const createMultiplier = (factor: number): Multiplier => {
  return (value: number): number => value * factor;
};

### React Component Props Callbacks

In React, callback props benefit from explicit types:

typescript
interface ButtonProps {
  onClick: (event: React.MouseEvent<HTMLButtonElement>) => void;
  label: string;
}

// Explicit return type matches interface
const Button = ({ onClick, label }: ButtonProps): JSX.Element => {
  return <button onClick={onClick}>{label}</button>;
};

### Generic Functions

Generic functions should always have explicit return types when the return type involves the generic:

typescript
// Explicit return type clarifies generic usage
const firstElement = <T>(arr: T[]): T | undefined => {
  return arr[0];
};

// Complex generic - explicit return type essential
const mapValues = <T, U>(
  obj: Record<string, T>,
  fn: (value: T) => U
): Record<string, U> => {
  const result: Record<string, U> = {};
  for (const key in obj) {
    result[key] = fn(obj[key]);
  }
  return result;
};

### Async Functions and Promises

Always explicitly type async function returns to avoid confusion:

typescript
// Implicit - returns Promise<number> but not obvious
const fetchCount = async () => {
  const response = await fetch("/api/count");
  return response.json();
};

// Explicit - clearly a Promise
const fetchCount = async (): Promise<number> => {
  const response = await fetch("/api/count");
  return response.json();
};

This helps catch errors where you forget to await or incorrectly type the resolved value.

Frequently asked questions

What does "Function expression requires a return type" mean?
This error is triggered by ESLint, not the TypeScript compiler itself. Specifically, it comes from the @typescript-eslint/explicit-function-return-type rule, which enforces explicit return type annotations on functions and function expressions. TypeScript's type inference is generally powerful enough to determine function return types automatically.
How do I fix "Function expression requires a return type"?
This ESLint error occurs when the @typescript-eslint/explicit-function-return-type rule is enabled and you define a function expression without explicitly annotating its return type. While TypeScript can infer return types, this linting rule enforces explicit annotations for better code clarity and type safety.
What causes "Function expression requires a return type"?
ESLint rule @typescript-eslint/explicit-function-return-type is enabled in your configuration Function expression (arrow function or function assigned to variable) lacks explicit return type annotation Strict TypeScript ESLint preset is active (recommended, recommended-type-checked, strict)
Is the "Function expression requires a return type" error serious?
It is a medium-severity error โ€” more of a blocker than a risk. The steps above resolve it.

Official resources & further reading