Typing Functions
Annotate parameters, returns, optional and default arguments, and functions passed as values.
What you'll learn
- Type optional and default parameters correctly
- Write a function type for a callback
- Explain when an explicit return type is worth writing
Prerequisites
Explanation
Functions are where types pay off most, because a function is a contract between code written at different times by different people.
Parameters and returns
function add(a: number, b: number): number {
return a + b;
}
Parameters always need annotations. The return type is usually inferred, so : number here is optional — but writing it is often worth it. An explicit return type makes the compiler check the function body against your intention, so if you later add a branch that returns a string, the error appears inside the function you broke rather than at some distant call site.
Optional and default parameters
function greet(name: string, greeting?: string): string {
return (greeting ?? "Hello") + ", " + name;
}
function greetWithDefault(name: string, greeting = "Hello"): string {
return greeting + ", " + name;
}
A ? parameter is string | undefined and must be handled. A default parameter is different: the type is inferred from the default and the parameter is never undefined inside the body, because the default fills in. Prefer defaults when a sensible one exists — it removes a branch.
Optional parameters must come after required ones. There is no way to skip an earlier argument.
Functions as values
To pass a function around, you need a type for it. The syntax is an arrow between parameters and return type:
type Formatter = (value: string) => string;
function applyTwice(value: string, f: Formatter): string {
return f(f(value));
}
Parameter names in a function type are documentation only — (value: string) => string and (input: string) => string are the same type. What matters is position, type, and count.
Callbacks are where this shows up constantly, and where inference helps: in items.map(x => x.length), TypeScript already knows x is a string because it knows items is string[]. You rarely annotate callback parameters inline.
void
void is the return type of a function that returns nothing useful:
function log(message: string): void {
console.log(message);
}
It means "do not rely on the return value", not "returns undefined and I promise nothing else ever will".
Example
A default parameter, a named function type, and a callback whose parameter is inferred.
type Transform = (value: string) => string;
function shout(value: string): string {
return value.toUpperCase() + "!";
}
function applyTwice(value: string, transform: Transform): string {
return transform(transform(value));
}
function joinNames(names: string[], separator = ", "): string {
return names.join(separator);
}
console.log(applyTwice("hey", shout));
console.log(joinNames(["Ada", "Grace", "Alan"]));
console.log(joinNames(["Ada", "Grace"], " & "));
// The callback parameter's type is inferred from the array's type:
const lengths = ["one", "three"].map((word) => word.length);
console.log(lengths.join("/"));Try it yourself
Change applyTwice's callback to one returning a number and Run — the mismatch is caught at the call site.
Code editor. Press Escape then Tab to leave the editor if keyboard focus becomes trapped. Press Control+Shift+M inside the editor to toggle Tab-key focus trapping.
Guided exercise
Guided exercise
Write `buildTag(content: string, tag = "p"): string` returning `"<p>hello</p>"` style markup, using a default parameter so `buildTag("hello")` works.
Checks: buildTag is defined · buildTag("hello") uses the default p tag · An explicit tag overrides the default
Code editor. Press Escape then Tab to leave the editor if keyboard focus becomes trapped. Press Control+Shift+M inside the editor to toggle Tab-key focus trapping.
Stuck? Get a hint.
Independent exercise
Independent exercise
Define `type NumberTransform = (n: number) => number`. Write `applyAll(values: number[], f: NumberTransform): number[]` that returns a new array with `f` applied to each value.
Checks: applyAll is defined · Applies the transform to every value · plus 1 hidden check
Code editor. Press Escape then Tab to leave the editor if keyboard focus becomes trapped. Press Control+Shift+M inside the editor to toggle Tab-key focus trapping.
Stuck? Get a hint.
Common mistakes
- Putting an optional parameter before a required one. Arguments are positional, so there is no way to skip one.
- Annotating callback parameters that TypeScript already infers from the array being mapped.
- Treating `void` as 'returns undefined'. It means the return value is not meant to be used.
Knowledge check
Takeaway
Parameters always need types; explicit return types localise errors to the function you actually broke.
Summary
Annotate parameters, prefer default values over optional parameters where a sensible default exists, and describe callbacks with `(params) => return` function types. Return types are inferred but often worth stating.
References
Your notes
Notes save automatically.
Finished this lesson?
Mark it complete to track your progress and schedule a future review.
AI tutor
The optional AI tutor isn't enabled in this deployment. All lessons, exercises, quizzes, and search work fully without it.