Question
In TypeScript, I can declare a function parameter using the Function type, but that does not seem type-safe. For example:
class Foo {
save(callback: Function): void {
// Do the save
const result: number = 42;
// Can TypeScript ensure at compile time that the callback
// accepts exactly one parameter of type number?
callback(result);
}
}
const foo = new Foo();
const callback = (result: string): void => {
alert(result);
};
foo.save(callback);
Here, save passes a number to the callback, but the callback expects a string, and using Function does not properly protect against that mismatch.
Is there a type-safe way to define a function parameter in TypeScript so the callback signature is checked at compile time?
In short: is there a TypeScript equivalent to a strongly typed delegate in .NET?
Short Answer
By the end of this page, you will understand why using Function is too broad in TypeScript, how to replace it with a typed function signature, and how TypeScript checks callback parameter types at compile time. You will also see common callback patterns, real-world usage, and how this compares to delegates in languages like C#.
Concept
In TypeScript, the type Function means some callable value, but it does not describe the function's parameter types or return type in a useful way.
That is why this is unsafe:
save(callback: Function): void
It tells TypeScript only that callback is a function. It does not tell TypeScript:
- how many arguments the function expects
- what types those arguments should be
- what the function returns
If you want type safety, you should use a function type signature.
For your example, the callback should accept one number and return void:
save(callback: (result: number) => void): void
Now TypeScript knows the exact shape of the callback. If you try to pass a function that expects a string instead of a number, TypeScript can report an error.
This matters because callbacks are everywhere in real programs:
Mental Model
Think of Function as writing "some tool" on a box label.
That tells you the box contains a tool, but not whether it is:
- a hammer
- a screwdriver
- a wrench
A typed function signature is like labeling the box with exact instructions:
- takes one number
- returns nothing
Now anyone using the box knows exactly what fits.
So:
Function= "this is a function somehow"(result: number) => void= "this function must take one number and return nothing"
The second label is what gives you safety.
Syntax and Examples
Core syntax
Use a function type instead of Function:
(result: number) => void
This means:
- the function takes one parameter named
result - that parameter must be a
number - the function returns
void
Your example, typed safely
class Foo {
save(callback: (result: number) => void): void {
const result = 42;
callback(result);
}
}
const foo = new Foo();
const goodCallback = (result: number): void => {
console.log(result);
};
foo.save(goodCallback);
Step by Step Execution
Consider this code:
class Foo {
save(callback: (result: number) => void): void {
const result = 42;
callback(result);
}
}
const foo = new Foo();
const callback = (result: number): void => {
console.log("Saved value:", result);
};
foo.save(callback);
Step by step:
Foodefines a method namedsave.saveexpects one argument: a function.- That function must match this shape:
- one parameter
- parameter type is
number - return type is
void
const result = 42;creates a number.
Real World Use Cases
1. API request callbacks
A function may fetch data, then pass the result to a callback:
type UserCallback = (userId: number) => void;
function loadUser(callback: UserCallback): void {
callback(101);
}
This prevents accidentally writing a callback that expects the wrong type.
2. Event processing
You may define custom event handlers:
type MessageHandler = (message: string) => void;
function onMessage(handler: MessageHandler): void {
handler("Hello");
}
3. Data transformation
Functions like map use typed callbacks:
Real Codebase Usage
In real projects, developers rarely use raw Function unless they truly mean "any function at all." Most of the time, they write exact function signatures.
Common patterns
Guarding callback inputs
type SaveCallback = (result: number) => void;
function saveValue(value: number, callback: SaveCallback): void {
if (value < 0) {
return;
}
callback(value);
}
The guard clause handles invalid state early, and the callback stays well typed.
Reusing callback types
type ErrorHandler = (message: string) => void;
type SuccessHandler = (id: number) => void;
Common Mistakes
1. Using Function instead of a real signature
Broken:
function save(callback: Function): void {
callback(42);
}
Why it is a problem:
Functionis too generic- parameter and return types are lost
Better:
function save(callback: (result: number) => void): void {
callback(42);
}
2. Forgetting the return type
Sometimes beginners type only the parameter list mentally, but not the full function shape.
Correct form:
(value: number) => void
Not just:
(value: )
Comparisons
| Approach | Example | Type-safe? | Best use |
|---|---|---|---|
Function | callback: Function | No | Rarely useful for app code |
| Inline function type | callback: (n: number) => void | Yes | Simple one-off callbacks |
| Type alias | type SaveCallback = (n: number) => void | Yes | Reusable callback types |
| Interface call signature | interface SaveCallback { (n: number): void } | Yes | Less common, but valid |
Function vs typed function signature
Cheat Sheet
// Avoid
callback: Function
// Prefer
callback: (value: number) => void
Common patterns
type Callback = (value: number) => void;
type Formatter = (value: number) => string;
type Validator = (text: string) => boolean;
Class method example
class Foo {
save(callback: (result: number) => void): void {
result = ;
(result);
}
}
FAQ
Is Function bad in TypeScript?
Not always, but it is usually too broad for callback parameters. It loses important information about arguments and return values.
How do I type a callback function in TypeScript?
Use a function signature such as:
(callback: (value: number) => void)
Can I create a reusable callback type?
Yes. Use a type alias:
type SaveCallback = (value: number) => void;
Is there a TypeScript equivalent to a C# delegate?
Yes. A typed function signature, often stored in a type alias, is the closest equivalent.
Should I use a type alias or an interface for function types?
Either works. For simple callbacks, many developers prefer type because it is shorter and clearer.
Why did my wrong callback still compile?
It may be because you used Function, or because your TypeScript configuration is not strict enough. Exact behavior can vary with compiler settings.
Mini Project
Description
Build a small TypeScript utility that simulates saving a score and then notifies the caller through a strongly typed callback. This demonstrates how to replace Function with a safe callback signature in a practical, reusable way.
Goal
Create a ScoreService class whose saveScore method accepts only callbacks that take a numeric score.
Requirements
- Create a class named
ScoreService. - Add a method
saveScorethat accepts a callback with onenumberparameter. - Inside the method, simulate saving a score and pass a number to the callback.
- Create one valid callback and call
saveScorewith it. - Add one invalid callback example in a comment to show what TypeScript should reject.
Keep learning
Related questions
Angular formGroup Error Explained: Fixing 'Can't bind to formGroup' in Reactive Forms
Learn why Angular shows 'Can't bind to formGroup' and how to fix it by importing ReactiveFormsModule correctly.
Fix "Element implicitly has an 'any' type" in TypeScript Object Indexing
Learn why TypeScript rejects string object indexing and how to fix it with keyof, unions, and typed object keys in React.
Fix "Property has no initializer" in Angular TypeScript Components
Learn why Angular TypeScript shows "Property has no initializer" and how to fix it using defaults, optional properties, or definite assignment.