Function types

Describe callable and constructable values with call signatures, construct signatures and parameters.

Signature
type Fn = (a: string) => void;                    // function type expression
type Described = { description: string; (n: number): boolean };  // call signature
type Ctor = { new (s: string): SomeObject };      // construct signature
interface CallOrConstruct { (n?: number): string; new (s: string): Date }
  • Added in TypeScript 1.0

Parameters

ParameterTypeDescription
$parameters(name: T, ...)Parameter list with names and types. The name is required in a function type expression: `(string) => void` means a parameter named `string` of type `any`.
$return type=> RWritten after `=>` in a function type expression, but after `:` inside a call or construct signature.
$optional parameteroptionala?: TMay be omitted by the caller; its type inside the body is `T | undefined`. Must follow all required parameters.
$rest parameteroptional...rest: T[]Absorbs the remaining arguments. May be typed with a tuple to check each forwarded position.
$new modifieroptionalnew (...)Marks a construct signature, describing a value usable with `new`.

Errors

tsc reports "Type '(x: string) => void' is not assignable to type '(ns: string | number) => void'" under `strictFunctionTypes`, and "Type 'X' has no construct signatures" when a value without a `new` signature is used with `new`.

Changelog

  • PHP 2.6`strictFunctionTypes` added: function-typed parameters are checked contravariantly. It deliberately does not apply to method syntax, which stays bivariant so that DOM and array hierarchies keep working.
  • PHP 3.0Rest parameters may be typed with tuple types, enabling checked argument forwarding and generic wrappers.
  • PHP 4.0Variadic tuple types make higher-order wrappers such as `(...args: Parameters<F>) => ReturnType<F>` practical without overloads.
  • PHP 5.0`const` type parameters allow a generic function to infer literal types from its arguments without the caller writing `as const`.

The compact form, (a: string) => void, covers most needs: a callback parameter, the type of a stored handler, an alias for a family of functions. The parameter name is required, and this trips people up more than anything else on this page — (string) => void is not "takes a string", it is a function with one parameter named string of type any.

Call and construct signatures

When a function also carries properties, or is callable in more than one way, switch to object-type syntax with a call signature: { description: string; (n: number): boolean }. Note the separator flips from => to :. Prefix a signature with new to describe something you can new, which is how you type a class passed around as a value or a factory that takes a constructor.

Bivariance under strictFunctionTypes

With strictFunctionTypes on, function-typed parameters are checked contravariantly: a handler for string is not accepted where a handler for string | number is required, because it would crash on a number. The rule applies only to function-syntax properties. Members declared with method syntax (handle(e: E): void) stay bivariant, so the unsound assignment silently compiles. If you want the check, declare the member as a property with an arrow type.

Example

Try it yourself
Loading editor…
Press Run to execute the code.

When to use it

  • Type a callback parameter so callers get argument names and return checking at the call site.
  • Describe a function that also has properties, such as a memoised helper with a `cache`.
  • Accept a class itself as an argument using a construct signature, for factories and DI containers.
  • Type a rest parameter with a tuple so a wrapper can forward arguments with full checking.

Frequently asked questions

Why does `(string) => void` accept anything I pass it?

Because it means "a function with one parameter named `string` of type `any`", not "a function taking a string". Parameter names are mandatory in function type expressions. Write `(s: string) => void` instead.

When do I use a call signature instead of the arrow form?

Use a call signature inside an object type when the function also has properties, has multiple overloads, or needs a construct signature alongside. The arrow form cannot express any of those. Remember that inside a call signature the return type follows a colon, not an arrow.

Why does TypeScript allow a narrower callback where a wider one is expected?

Because the member was declared with method syntax, which stays bivariant even under `strictFunctionTypes`. That exemption exists so that pre-existing class hierarchies, including the DOM's, still type-check. Declare the member as a property with an arrow type to get contravariant checking.

More examples

Method syntax is bivariant, property syntax is not

Prints "narrow: OK" then "prop: 42". The first assignment is the unsound one — `narrow` only handles strings, yet it now sits behind a signature that promises to accept numbers, and calling `h.handleMethod(42)` would throw at runtime. tsc allows it because the member uses method syntax. The commented line is the same mistake written as a property, and that one is rejected.

Example · typescript
class Handler {
  // Method syntax: parameters are checked bivariantly, even under strictFunctionTypes.
  handleMethod(e: string | number) { console.log("method:", e); }
  // Function-property syntax: parameters are checked contravariantly.
  handleProp: (e: string | number) => void = (e) => console.log("prop:", e);
}

const h = new Handler();
const narrow = (e: string) => console.log("narrow:", e.toUpperCase());

h.handleMethod = narrow;   // allowed: method bivariance
// h.handleProp = narrow;  // tsc error under strictFunctionTypes
h.handleMethod("ok");
h.handleProp(42);

Construct signatures for a factory

Prints "9.00 28.27". `ShapeCtor` describes the class itself rather than an instance, so `build` can accept `Square` or `Circle` as values and `new` them. A class whose constructor takes different arguments, or whose instances lack `area`, is rejected at the call site rather than inside `build`.

Example · typescript
interface Shape { area(): number }
type ShapeCtor = new (size: number) => Shape;

class Square implements Shape {
  constructor(private size: number) {}
  area() { return this.size ** 2; }
}
class Circle implements Shape {
  constructor(private size: number) {}
  area() { return Math.PI * this.size ** 2; }
}

function build(Ctor: ShapeCtor, size: number): string {
  return new Ctor(size).area().toFixed(2);
}

console.log(build(Square, 3), build(Circle, 3));

Discussion

  • Be the first to comment on this lesson.