Skip to content
TypeScript ts functions 3 min read

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
}
FormCallableExtra propertiesOverloads
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, and ConstructorParameters to derive types from such signatures.
  • Keep callable interfaces small and well-named so the dual nature (call + properties) stays obvious.
Last updated June 29, 2026
Was this helpful?