Call & Construct Signatures
A function type expression like (x: number) => string is enough for most callbacks, but it cannot describe a function that also has properties, that can be invoked in multiple ways, or that is meant to be called with new. For those cases TypeScript offers call signatures and construct signatures — member-style declarations written inside an object type or interface. This page covers callable objects, overloaded call signatures, new (construct) signatures, and how the two combine to describe classes and factories.
Call signatures
A call signature looks like a method without a name: (params): ReturnType written as a member. It lets an object type be both callable and carry additional properties.
interface Counter {
(label: string): number; // call signature
count: number; // extra property
reset(): void; // method
}
function makeCounter(): Counter {
const fn = ((label: string) => {
fn.count += 1;
return fn.count;
}) as Counter;
fn.count = 0;
fn.reset = () => {
fn.count = 0;
};
return fn;
}
const c = makeCounter();
c("first"); // 1
c.count; // 1
c.reset();
The bare (label: string): number member is what makes c(...) callable, while count and reset describe the properties hanging off the function.
Call signature vs function type expression
Both describe a callable value, but only a call signature inside an interface or object type can add properties or multiple signatures.
type Fn = (x: number) => number; // function type expression
interface Fn2 {
(x: number): number; // equivalent call signature
}
| Form | Callable | Extra properties | Overloads |
|---|---|---|---|
type F = (x) => R | ✅ | ❌ | ❌ |
interface F { (x): R } | ✅ | ✅ | ✅ |
Use the type expression for plain callbacks; reach for an interface (or object type) with a call signature when the function needs more than its call.
Overloaded call signatures
Listing several call signatures in a row models a function that behaves differently based on its arguments — the interface form of function overloading.
interface Parser {
(input: string): object;
(input: string, reviver: (k: string, v: unknown) => unknown): object;
}
const parse: Parser = (input: string, reviver?: (k: string, v: unknown) => unknown) =>
JSON.parse(input, reviver);
Callers see two valid ways to call parse; the single implementation must be compatible with every listed signature.
Construct signatures
Prefix a signature with new to describe something callable with new — a constructor or class. This types factories and dependency-injection containers that receive classes rather than instances.
interface Animal {
name: string;
}
interface AnimalConstructor {
new (name: string): Animal; // construct signature
}
class Dog implements Animal {
constructor(public name: string) {}
}
function create(ctor: AnimalConstructor, name: string): Animal {
return new ctor(name);
}
create(Dog, "Rex"); // ✅ Dog matches new (name: string) => Animal
AnimalConstructor accepts any value you can write new value(name) on, returning an Animal. Passing a plain function with no new capability is rejected.
Combining call and construct signatures
Some values — notably built-ins like Date — can be called both with and without new. An interface may declare both signatures.
interface DateLike {
(): string; // Date() → string
new (value: number): Date; // new Date(ms) → Date
}
Use a construct signature when an API receives a class itself. For typing a class’s instances you usually just use the class name as a type; reach for
new (...)only when the constructor is the value being passed around.
Best Practices
- Use a function type expression for plain callbacks; switch to a call signature when the function also needs properties.
- Model overloaded functions with multiple call signatures in an interface, or use overload declarations.
- Use
new (...)construct signatures to type values passed as classes/constructors, not their instances. - Combine call and construct signatures only for values genuinely usable both ways (like
Date). - Prefer the built-in utility types
Parameters,ReturnType, andConstructorParametersto derive types from such signatures. - Keep callable interfaces small and well-named so the dual nature (call + properties) stays obvious.