Function types
In JavaScript a function is a value, like a number or a string. You put it in a variable, pass it to another function, keep it in an array. But what type does a variable holding a function have?
Picture a wall of vending machines at a station. Above one empty spot hangs a plate, "Takes two coins, gives a cup", and the machine itself is not there yet. Any machine that takes two coins and gives a cup can go there — no matter who built it or what is inside. A function type is such a plate without a machine: it describes which parameters a function takes and what it returns, and the function itself is put in later.
Writing a function type
Hover over add in the line const add = (a: number, b: number) => a + b;. The hover shows const add: (a: number, b: number) => number. The part after the colon is the function type. You can name it with type, like any other type:
type Op = (a: number, b: number) => number;
const add: Op = (a: number, b: number) => a + b;
const mul: Op = (x: number, y: number) => x * y;
function apply(op: Op, a: number, b: number): number {
return op(a, b);
}
console.log(apply(add, 2, 3), apply(mul, 2, 3)); // 5 6Let's take (a: number, b: number) => number apart:
- the parentheses hold the parameters with their types, as in a function signature;
- after the arrow
=>comes the result type: in a function type it follows the arrow, not a colon; - the names
aandbare only for the reader:mulnames its parametersxandy, and it still fitsOp.
Now the example line by line:
type Opgives the plate a name: "takes two numbers, returns a number".addandmulare two different functions, and both fitOp.- The parameter
op: Opinapplyaccepts any function of this type. apply(add, 2, 3)callsaddand returns5, andapply(mul, 2, 3)returns6.
A function of the wrong kind
If a function's parameters or result don't match the plate, tsc won't let you put it in the variable:
type Op = (a: number, b: number) => number;
const glue: Op = (a: string, b: string) => a + b; // ✗error TS2322: Type '(a: string, b: string) => string' is not assignable to type 'Op'.The message means: "a function of type (a: string, b: string) => string cannot be assigned to the type Op". apply will pass it two numbers, while the function expects strings. In the message tsc shows the function's type in the same arrow notation.
This is where the plate analogy ends. A machine's plate gives the coins no names. The parameters in a function type do have names, but tsc ignores them: it compares only the parameter types and the result type.
void in a function type
In the third theme a function with the annotation : void could not return a value: tsc answered with error TS2322. In a function type void behaves differently:
type Callback = (n: number) => void;
const saved: number[] = [];
const keep: Callback = (n: number) => saved.push(n);
const r = keep(5);
console.log(r, saved.length); // 1 1The push method returns the array's new length, so the arrow returns a number. Still, it fits Callback, and at run time keep(5) returns 1. But r has the type void, so r + 1 gives error TS2365: the result of such a function must not be used. Passing r to console.log is allowed, since it accepts a value of any type, but tsc won't let you compute with it or call its methods.
void in a function type means "nobody uses the result", not "the function returns nothing". That makes callbacks convenient: the forEach method expects a function with a void result, and the arrow (n: number) => saved.push(n) fits it, even though push returns something. Interviewers ask about it like this: "Why does a function that returns a number fit the type () => void?"
Common mistake
The result type was written after a colon, as in a function declaration:
type Op = (a: number, b: number): number; // ✗error TS1005: '=>' expected.The message means: "an arrow => was expected". A colon before the result type belongs in a function declaration, and a function type uses the arrow: (a: number, b: number) => number.
Key points
- A function type is written as
(a: number, b: number) => number: the parameters with their types, an arrow, the result type. - Any function with the same parameter and result types fits a function type; parameter names don't matter.
voidin a function type means "the result is not used": a function that returns a value fits too.- In a function type the result type goes after
=>, not after a colon.