Step 1 of 5 · Reading · ~3 min
Learn
Functions
Function Types
In TypeScript a function has a type: the types of its parameters and of its return value. By the end of this lesson you can annotate functions, make parameters optional or defaulted, collect extra arguments, and treat functions as values that you pass to other functions and return from them.
Annotating functions
function area(width: number, height: number): number {
return width * height;
}
const perimeter = (width: number, height: number): number => 2 * (width + height);
Annotate parameters always: the compiler cannot guess them. The return type can be inferred from the body, but writing it down documents the intent and catches a body that returns the wrong thing.
Optional, default and rest parameters
function greet(name: string, greeting?: string): string {
return `${greeting ?? "Hello"}, ${name}!`;
}
greet("Ana"); // "Hello, Ana!"
greet("Ana", "Howdy"); // "Howdy, Ana!"
? makes a parameter optional; inside the function its type is string | undefined. The expression greeting ?? "Hello" means "greeting, unless it is null or undefined, in which case "Hello"" (the records lesson returns to ??). Optional parameters must come after required ones. A default value makes a parameter optional too, and inside the function its type is plain string:
function pad(text: string, width: number, fill: string = " "): string {
return text.padStart(width, fill);
}
A rest parameter gathers all remaining arguments into an array, and a spread at the call site does the opposite, turning an array into separate arguments:
function total(...prices: number[]): number {
let sum = 0;
for (const p of prices) sum += p;
return sum;
}
total(2, 3, 5); // 10
const cart = [4, 6];
total(...cart); // 10: spread the array into arguments
total(); // 0: a rest parameter can receive nothing
Functions are values
A function can be stored in a variable, passed as an argument, and returned. The type of such a value is written (parameters) => result, and a type alias gives it a name:
type Op = (x: number) => number;
function applyTwice(f: Op, x: number): number {
return f(f(x));
}
function makeAdder(k: number): Op {
return (x) => x + k; // remembers k: this is a closure
}
const addFive: Op = makeAdder(5);
console.log(applyTwice(addFive, 1)); // 11
console.log(applyTwice((x) => x * 3, 2)); // 18
makeAdder returns a new function each time, and each one remembers its own k. Passing addFive without parentheses hands over the function itself; addFive(1) would call it and hand over the result. A callback's parameter types are usually inferred from the expected Op, so (x) => x * 3 needs no annotation.
void and never
A function that returns nothing useful is typed void; a function that never returns normally, because it throws or loops forever, is typed never:
function fail(message: string): never {
throw new Error(message);
}
A callback typed to return void may be given a function that returns something; the result is simply ignored. That is why [1, 2].forEach(n => list.push(n)) type-checks even though push returns a number.
Overloads, briefly
When one function does genuinely different things for different argument types, you write several signatures above a single implementation. Callers see only the signatures. Most code does not need them; a union parameter is usually simpler.
Common mistakes
- Required after optional.
function f(a?: string, b: number)is a compile error; optional parameters come last. - Calling when you meant to pass.
applyTwice(addFive(1), 2)passes a number where a function is expected. - Mutating an argument you were handed. A rest array like
pricesis yours, but calling.reverse()or.sort()on an array someone else passed changes their array. - Forgetting the empty case.
total()receives an empty array; make sure your function returns something sensible, notundefined.
Your exercise
Build the function-combining tool described below. You receive a list of small number-to-number functions and must return ONE function that applies them all. The starter supplies the table of operations and does the input and output. The two mistakes the hidden tests catch are applying the functions in the wrong order and mishandling an empty list.
Discussion
Ask a question, share an insight, or help someone who’s stuck.
Sign in to post a comment or reply.
Loading…