Question

How to Type Function Callbacks in TypeScript Interfaces

Question

I have this TypeScript interface:

interface Param {
    title: string;
    callback: any;
}

I want callback to be typed as a function, without using the universal any type. I tried something like this:

interface Param {
    title: string;
    callback: function;
}

but TypeScript does not accept it. How should I properly define the type for a callback function used as a property in an interface?

Short Answer

By the end of this page, you will understand how to type callback functions in TypeScript interfaces using function signatures, when to use Function, why it is usually too broad, and how to define safer callback types with parameters and return values.

Concept

In TypeScript, functions have their own types. You do not usually write function as a type name. Instead, you describe a function by its parameter list and return type.

For example:

(type for a function) => returnType

A callback is just a function passed to another part of your program so it can be called later. Because TypeScript checks types, it wants to know:

  • What arguments the callback receives
  • What the callback returns

That is why this is valid:

interface Param {
    title: string;
    callback: () => void;
}

This means:

  • callback is a function
  • it takes no arguments
  • it returns nothing (void)

If you truly want “any function” without describing its exact shape, TypeScript has a built-in Function type:

interface Param {
    title: string;
    callback: Function;
}

However, Function is usually not the best choice because it is too broad. It tells TypeScript almost nothing useful about how the function should be called.

In real code, a specific callback signature is much better because it gives:

  • autocomplete
  • safer calls
  • clearer intent
  • better error checking

So the best practice is usually to define the callback with a proper function signature instead of using any, function, or even Function.

Mental Model

Think of a callback type like a job description.

  • any means: “This worker can be anything.”
  • Function means: “This worker is at least a person, but I do not know their skills.”
  • () => void means: “This worker takes no input and performs a task.”
  • (name: string) => number means: “This worker receives a string and gives back a number.”

The more precise the job description, the easier it is to use the worker correctly.

If you only say “function,” that is too vague for TypeScript. It wants the shape of the function, not just the general idea that it is callable.

Take Quiz

Syntax and Examples

Basic function type syntax

A function type is written like this:

(param1: Type1, param2: Type2) => ReturnType

Example: callback with no arguments

interface Param {
    title: string;
    callback: () => void;
}

const item: Param = {
    title: "Save",
    callback: () => {
        console.log("Saved");
    }
};

Here:

  • callback takes no arguments
  • callback returns void

Example: callback with arguments

interface Param {
    title: string;
    callback: (message: string) => void;
}

const item: Param = {
    title: "Notify",
    callback: (message) => {
        console.log(message);
    }
};

Now the callback must accept a string.

Example: callback with a return value

interface Param {
    title: string;
    callback: (value: number) => boolean;
}

const item: Param = {
    title: "Check",
    callback: (value) => {
        return value > 10;
    }
};

This callback takes a number and returns a boolean.

If you want any function at all

interface Param {
    title: string;
    callback: Function;
}

This works, but it is less safe than a specific signature.

Using a named type alias

For readability, you can extract the callback type:

type Callback = (message: string) => void;

interface Param {
    title: string;
    callback: Callback;
}

This is common in larger codebases because it makes the interface easier to read and reuse.

Step by Step Execution

Consider this example:

interface Param {
    title: string;
    callback: (count: number) => string;
}

const param: Param = {
    title: "Counter",
    callback: (count) => {
        return `Count is ${count}`;
    }
};

const result = param.callback(3);
console.log(result);

Step by step:

  1. TypeScript reads the interface.

    • title must be a string.
    • callback must be a function that takes a number and returns a string.
  2. The param object is checked against the interface.

    • title: "Counter" is valid because it is a string.
    • callback: (count) => { ... } is checked.
  3. TypeScript verifies the callback.

    • It sees one parameter: count
    • It expects count to be a number
    • It expects the function to return a string
  4. param.callback(3) is valid.

    • 3 is a number
    • the callback returns Count is 3
  5. result becomes a string.

If you tried this instead:

param.callback("hello");

TypeScript would report an error because the callback expects a number, not a string.

Real World Use Cases

Callback types appear everywhere in TypeScript applications.

Event handlers

interface ButtonConfig {
    label: string;
    onClick: () => void;
}

Used in UI code when a button should run some logic.

Data processing

interface Processor {
    transform: (input: string) => string;
}

Used when formatting text, cleaning data, or converting values.

API response handling

interface RequestOptions {
    onSuccess: (data: string) => void;
    onError: (message: string) => void;
}

Used when different functions handle successful and failed requests.

Array utilities

Methods like map, filter, and forEach all use callbacks:

const numbers = [1, 2, 3];
const doubled = numbers.map((n) => n * 2);

TypeScript uses callback types to ensure map is called correctly.

Take Quiz

Real Codebase Usage

In real projects, developers rarely leave callback types vague unless they truly have no constraints.

Common pattern: define exact signatures

interface Task {
    run: () => Promise<void>;
}

This makes usage predictable.

Common pattern: reusable type aliases

type ErrorHandler = (error: Error) => void;

type SuccessHandler = (message: string) => void;

interface Handlers {
    onSuccess: SuccessHandler;
    onError: ErrorHandler;
}

This avoids repeating long function signatures.

Common pattern: optional callbacks

interface Config {
    title: string;
    onComplete?: () => void;
}

The ? means the callback may or may not be provided.

Common pattern: validation callbacks

interface FieldRule {
    validate: (value: string) => boolean;
}

This is common in forms and input checking.

Common pattern: async callbacks

interface Job {
    execute: () => Promise<string>;
}

Used when the callback performs asynchronous work.

Guarding optional callbacks before calling

function finish(config: Config) {
    if (config.onComplete) {
        config.onComplete();
    }
}

This is safer than assuming the function always exists.

Common Mistakes

1. Using function as a type

This is invalid TypeScript:

interface Param {
    callback: function;
}

Why it fails:

  • function is not a valid type keyword in TypeScript type annotations.
  • You must use a function signature like () => void or the broad Function type.

2. Using any for callbacks

interface Param {
    callback: any;
}

Why it is risky:

  • TypeScript cannot protect you
  • You can call the callback incorrectly
  • Errors move from compile time to runtime

3. Using Function when a specific signature is better

interface Param {
    callback: Function;
}

This is allowed, but weak.

Problem:

const p: Param = {
    callback: (x: number) => x * 2
};

Later, someone might try:

p.callback("wrong input");

TypeScript gives much less help because Function is too general.

4. Forgetting the return type

Broken example:

interface Param {
    callback: (name: string);
}

Correct version:

interface Param {
    callback: (name: string) => void;
}

A function type needs both:

  • parameter list
  • return type

5. Mismatching the callback implementation

interface Param {
    callback: (value: number) => string;
}

const p: Param = {
    callback: (value) => value * 2
};

Problem:

  • value * 2 returns a number
  • the interface expects a string

Correct version:

const p: Param = {
    callback: (value) => String(value * 2)
};

Comparisons

ApproachExampleValid?Type SafetyBest Use
anycallback: anyYesVery lowAvoid when possible
functioncallback: functionNoNoneInvalid in TypeScript
Functioncallback: FunctionYesLowOnly when any callable value is acceptable
Specific function typecallback: () => voidYesHighBest for most cases
Named type aliascallback: CallbackYesHighBest when reused

Function vs specific signature

callback: Function
  • accepts any function
  • gives poor information about parameters and return value
callback: (id: number) => string
  • clearly describes the callback
  • safer to call
  • easier to maintain

Inline type vs type alias

Inline:

interface Param {
    callback: (value: string) => boolean;
}

Type alias:

type Callback = (value: string) => boolean;

interface Param {
    callback: Callback;
}

Use inline types for simple one-off cases. Use type aliases when the same callback shape appears in multiple places.

Take Quiz

Cheat Sheet

// Best practice: define the exact callback signature
interface Param {
    title: string;
    callback: () => void;
}
// Callback with parameters
interface Param {
    title: string;
    callback: (message: string) => void;
}
// Callback with return value
interface Param {
    title: string;
    callback: (value: number) => boolean;
}
// Broad function type (allowed, but not ideal)
interface Param {
    title: string;
    callback: Function;
}

Rules

  • Do not use function as a type
  • Prefer a specific function signature over any
  • Prefer a specific function signature over Function
  • A function type uses this form:
    • (params) => returnType
  • Use void when nothing is returned
  • Use ? for optional callbacks

Common patterns

onClick: () => void
onChange: (value: string) => void
validate: (input: string) => boolean
load: () => Promise<Data>

Reusable alias

type Callback = (value: string) => void;

Edge case

If you do not know the exact signature yet, Function is possible, but it removes much of TypeScript's safety.

FAQ

Why is function not accepted as a type in TypeScript?

Because function is not a valid type annotation keyword. TypeScript expects either a function signature like () => void or a built-in type such as Function.

Should I use Function for callbacks in TypeScript?

Only if you truly want any callable function and do not care about parameters or return type. In most cases, a specific function signature is better.

What is the correct way to type a callback with no arguments?

Use:

() => void

This means the function takes no arguments and returns nothing.

How do I type a callback that returns a value?

Use a return type after the arrow:

(value: number) => string

Can I make a callback optional in an interface?

Yes:

interface Param {
    callback?: () => void;
}

Is any the same as Function?

No. any disables type checking almost completely. Function at least says the value should be callable, but it is still very broad.

When should I use a type alias for a callback?

Use a type alias when the same callback type is reused or when the inline signature is getting too long.

Take Quiz

Mini Project

Description

Build a small TypeScript configuration object for a notifier system. Each notifier has a title and a callback function that runs when a message is sent. This demonstrates how to define and use callback types safely in an interface.

Goal

Create a typed interface with a callback property and use it to run functions with the correct parameter and return types.

Requirements

  • Create an interface with a title property and a typed callback property.
  • The callback must accept a string message.
  • The callback must return a string.
  • Create at least two objects that follow the interface.
  • Call each callback and log the result.
Take Quiz

Keep learning

Related questions

View more