Function types
Describe callable and constructable values with call signatures, construct signatures and parameters.
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
| Parameter | Type | Description |
|---|---|---|
$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 | => R | Written after `=>` in a function type expression, but after `:` inside a call or construct signature. |
$optional parameteroptional | a?: T | May 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 modifieroptional | new (...) | 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
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.
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`.
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