How to fix Function type '() => void' is not assignable to function type '() => undefined' in TypeScript

TypeScriptINTERMEDIATEMEDIUMUpdated Dec 10, 2025· 110 views

This TypeScript error occurs when you try to assign a function with a 'void' return type to a function type that expects 'undefined'. While both represent 'no useful return', TypeScript treats them differently: 'void' means 'ignores any return value', while 'undefined' means 'must explicitly return undefined'. The fix involves adjusting function signatures or using type assertions.

Quick fix

Fixes TS2322

// BEFORE - causes error
type Handler = () => undefined;
const myHandler: Handler = () => {
  console.log("Hello");
  // Implicitly returns void, not undefined
};

// AFTER - fix by adding explicit return
type Handler = () => undefined;
const myHandler: Handler = () => {
  console.log("Hello");
  return undefined; // Explicitly return undefined
};

// OR change the type to void if appropriate
type Handler = () => void;
const myHandler: Handler = () => {
  console.log("Hello");
  // No return needed for void
};
  1. Change function return type from 'void' to 'undefined'
  2. Use type assertion when you know the assignment is safe
  3. Adjust interface or type definitions
  4. Create wrapper functions when dealing with third-party libraries
typescript — error
// BEFORE - causes error
type Handler = () => undefined;
const myHandler: Handler = () => {
console.log("Hello");
// Implicitly returns void, not undefined
};
// AFTER - fix by adding explicit return
type Handler = () => undefined;
const myHandler: Handler = () => {
console.log("Hello");
return undefined; // Explicitly return undefined
};
// OR change the type to void if appropriate
type Handler = () => void;
const myHandler: Handler = () => {
console.log("Hello");
// No return needed for void
};
Function type '() => void' is not assignable to function type '() => undefined'

What this error means

The error "Function type '() => void' is not assignable to function type '() => undefined'" highlights a subtle but important distinction in TypeScript's type system between 'void' and 'undefined' return types. In JavaScript, a function without a return statement implicitly returns 'undefined'. However, TypeScript distinguishes between: - **'void'**: Means "this function doesn't return anything useful" - any return value is ignored - **'undefined'**: Means "this function returns the specific value 'undefined'" This distinction exists because TypeScript's 'void' has special behavior: when a function type is declared to return 'void', you can actually assign functions that return any value, but that value will be treated as 'void' at the call site. This enables patterns like 'Array.prototype.forEach' which expects a callback returning 'void' but can accept functions that return values. The error occurs when TypeScript's strict type checking prevents assigning a 'void' function to an 'undefined' function type, as they're not considered equivalent despite both representing "no useful return value".

Typical symptoms

  • Compilation fails with 'Function type () => void is not assignable to function type () => undefined'
  • IDE shows red underlines on function assignments or type declarations
  • Error code TS2322 appears in compiler output (type mismatch)
  • Code works when using 'any' type but fails with strict typing
  • Function callbacks or event handlers fail type checking
  • Library functions expecting specific return types reject your implementations

Common causes

  1. Assigning a function declared with ': void' return type to a variable expecting ': () => undefined'
  2. Implementing an interface or type that requires 'undefined' return with a function that returns 'void'
  3. Using 'void' in callback types when the API expects 'undefined'
  4. Mismatch between function declarations and implementations in type definitions
  5. Library type definitions that incorrectly use 'undefined' instead of 'void'
  6. Trying to assign event handlers or callbacks with incompatible return types
  7. Generic constraints that differentiate between 'void' and 'undefined'

How to fix "Function type '() => void' is not assignable to function type '() => undefined'"

1Change function return type from 'void' to 'undefined'

If you control the function signature, change it from 'void' to 'undefined':

typescript
// BEFORE - causes error
type Handler = () => undefined;
const myHandler: Handler = () => {
  console.log("Hello");
  // Implicitly returns void, not undefined
};

// AFTER - fix by adding explicit return
type Handler = () => undefined;
const myHandler: Handler = () => {
  console.log("Hello");
  return undefined; // Explicitly return undefined
};

// OR change the type to void if appropriate
type Handler = () => void;
const myHandler: Handler = () => {
  console.log("Hello");
  // No return needed for void
};

Key difference:
- 'void' functions can have no return statement or 'return;' (no value)
- 'undefined' functions must explicitly 'return undefined'

2Use type assertion when you know the assignment is safe

If you're certain the assignment is safe (function doesn't return anything), use a type assertion:

typescript
// Function that doesn't return anything (void)
const logMessage = (msg: string): void => {
  console.log(msg);
};

// Type expecting undefined return
type Callback = () => undefined;

// Type assertion tells TypeScript to trust you
const callback: Callback = logMessage as () => undefined;

// Alternative syntax
const callback2: Callback = logMessage as unknown as () => undefined;

// Use with caution - only when you're sure the function
// won't be used in a context that depends on undefined return

Warning: Type assertions bypass TypeScript's type checking. Only use when:
1. You know the function truly doesn't return anything
2. The calling code doesn't depend on receiving 'undefined'
3. You've verified runtime behavior matches the assertion

3Adjust interface or type definitions

If you control the type definitions, consider whether 'void' is more appropriate than 'undefined':

typescript
// BEFORE - overly restrictive
interface EventEmitter {
  on(event: string, handler: () => undefined): void;
}

// AFTER - use void for callbacks that don't need to return values
interface EventEmitter {
  on(event: string, handler: () => void): void;
}

// Example usage with void
const emitter: EventEmitter = {
  on: (event, handler) => {
    // handler can return anything, we ignore it
    handler();
  }
};

emitter.on("click", () => {
  console.log("Clicked!");
  // Can return a value (ignored) or nothing
  return 42; // Allowed with void
});

General guideline:
- Use 'void' for callbacks, event handlers, side effects
- Use 'undefined' only when the return value must be exactly undefined
- Most APIs should use 'void' for better compatibility

4Create wrapper functions when dealing with third-party libraries

When a library incorrectly uses 'undefined' instead of 'void', create a wrapper:

typescript
// Library with incorrect type
declare module "bad-library" {
  export type BadCallback = () => undefined;
  export function register(callback: BadCallback): void;
}

// Your wrapper function
function wrapCallback(callback: () => void): () => undefined {
  return () => {
    callback();
    return undefined; // Explicit return to satisfy undefined type
  };
}

// Usage
import { register } from "bad-library";

const myHandler = () => {
  console.log("Handling event");
  // No return - void function
};

// Wrap to satisfy library's incorrect type
register(wrapCallback(myHandler));

// Alternative: inline wrapper
register(() => {
  myHandler();
  return undefined;
});

This approach maintains type safety while working around library limitations.

5Use conditional types or overloads for flexible APIs

For library authors, design flexible APIs that accept both 'void' and 'undefined':

typescript
// Method 1: Overloads
function register(callback: () => void): void;
function register(callback: () => undefined): void;
function register(callback: (() => void) | (() => undefined)): void {
  // Implementation
  const result = callback();
  // result is void | undefined
}

// Method 2: Generic with conditional type
type CallbackReturn<T> = T extends void ? void : undefined;

function register<T extends void | undefined>(
  callback: () => T
): void {
  callback();
}

// Both work
register(() => { console.log("void"); });
register(() => { console.log("undefined"); return undefined; });

// Method 3: Union type (simplest)
type FlexibleCallback = (() => void) | (() => undefined);

function register(callback: FlexibleCallback): void {
  callback();
}

These patterns make your APIs more flexible and user-friendly.

6Understand and leverage void's special behavior

TypeScript's 'void' has unique behavior that can help avoid this error:

typescript
// void functions can return values in certain contexts
type VoidFunc = () => void;

// These are all valid!
const f1: VoidFunc = () => true;
const f2: VoidFunc = () => "hello";
const f3: VoidFunc = () => 42;
const f4: VoidFunc = () => ({ key: "value" });

// When called, the return values are treated as void
const result = f1(); // result has type void

// This enables common patterns
const numbers = [1, 2, 3];
const results = numbers.map(n => n * 2); // returns number[]
numbers.forEach(n => n * 2); // forEach expects (n) => void

// Key insight: If an API expects () => void, you can give it
// a function that returns anything, and it will work.
// The reverse is not true: () => void cannot be assigned to () => undefined.

// Use this knowledge to design better types:
// When in doubt, use 'void' for maximum compatibility

Remember: 'void' is more permissive than 'undefined' in assignment contexts.

Advanced notes

### The Type Theory Behind void vs undefined

TypeScript's treatment of 'void' and 'undefined' comes from their different roles in the type system:

undefined is a unit type (a type with exactly one value: 'undefined'). It's a proper value that can be assigned, returned, and checked.

void is a bottom-like type in certain contexts. It represents the absence of a meaningful value. The key insight is that 'void' is assignable from any type in function return position (when the function type is contextual), but values of type 'void' cannot be used.

This duality enables:
- Array.prototype.forEach(callback: (value) => void) to accept callbacks that return values
- Type safety by preventing use of ignored return values
- Clean separation between pure functions (return values) and effectful functions (return void)

### Historical Context and JavaScript Compatibility

The distinction exists because TypeScript must balance:
1. JavaScript reality: Functions without return statements return 'undefined'
2. Type safety: Distinguishing intentional 'undefined' returns from ignored values
3. Practical patterns: Allowing 'forEach' and similar APIs to work with any callback

In early TypeScript versions, 'void' and 'undefined' were more interchangeable. As TypeScript added stricter checks, this distinction became more important.

### Compiler Flags and Strictness

The strictness of void/undefined checking depends on compiler flags:

- strictFunctionTypes: When enabled (default in strict mode), function type checking is contravariant in parameter types, which affects how void/undefined are compared
- strictNullChecks: Required for proper undefined checking
- noImplicitReturns: Can help catch missing 'return undefined' statements

### Real-World Examples and Patterns

React Event Handlers:

typescript
// React typically uses void for event handlers
type ClickHandler = (event: React.MouseEvent) => void;

const handleClick: ClickHandler = (event) => {
  event.preventDefault();
  // No return needed - void
};

Node.js Callbacks:

typescript
// Node.js style callbacks use void
function readFile(path: string, callback: (err: Error | null, data: Buffer) => void): void {
  // Implementation
}

Functional Programming:

typescript
// void for side effects, undefined for explicit absence
const tap = <T>(fn: (value: T) => void) => (value: T): T => {
  fn(value);
  return value;
};

// Usage
const result = [1, 2, 3]
  .map(x => x * 2)
  .reduce(tap(console.log), 0); // tap returns void

### When to Use Each

Use 'void' when:
- Function performs side effects
- Return value is ignored/unimportant
- Implementing callbacks, event handlers
- Following common library patterns (React, Node.js)

Use 'undefined' when:
- Function must explicitly return undefined (rare)
- Modeling JavaScript's implicit return behavior
- Working with legacy code that depends on undefined returns
- Creating precise type definitions for JavaScript interop

### Migration Strategy

If you encounter this error in a large codebase:
1. First, use 'void' in type definitions (more permissive)
2. Add explicit 'return undefined' where needed
3. Gradually fix library definitions to use 'void'
4. Use ESLint rules to catch problematic patterns

Remember: 'void' is almost always the better choice for new code due to its flexibility and alignment with common JavaScript patterns.

Frequently asked questions

What does "TS2322" mean?
The error "Function type '() => void' is not assignable to function type '() => undefined'" highlights a subtle but important distinction in TypeScript's type system between 'void' and 'undefined' return types. In JavaScript, a function without a return statement implicitly returns 'undefined'. However, TypeScript distinguishes between: - 'void': Means "this function doesn't return anything useful" - any return value is ignored - 'undefined': Means "this function returns the specific value 'undefined'" This distinction exists because TypeScript's 'void' has special behavior: when a function type is declared to return 'void', you can actually assign functions that return any value, but that value will be treated as 'void' at the call site.
How do I fix "TS2322"?
This TypeScript error occurs when you try to assign a function with a 'void' return type to a function type that expects 'undefined'. While both represent 'no useful return', TypeScript treats them differently: 'void' means 'ignores any return value', while 'undefined' means 'must explicitly return undefined'. The fix involves adjusting function signatures or using type assertions.
What causes "TS2322"?
Assigning a function declared with ': void' return type to a variable expecting ': () => undefined' Implementing an interface or type that requires 'undefined' return with a function that returns 'void' Using 'void' in callback types when the API expects 'undefined'
Is the "TS2322" error serious?
It is a medium-severity error — more of a blocker than a risk. The steps above resolve it.

Official resources & further reading