Chapter 4 of 13
~14 minFunctions & Type Annotations
Typing a function means describing both what it accepts and what it returns, which lets the compiler catch a wrong argument or a misused result at every call site, not just inside the function body itself.
Typing Parameters and Return Values
Each parameter gets its own annotation, and the return type is written after the parameter list. TypeScript can usually infer the return type from the function body, but writing it explicitly on exported or public functions is good practice: it documents intent and prevents an accidental change inside the function from silently widening its return type for every caller.
Try it yourself
function add(a: number, b: number): number { return a + b; } const multiply = (a: number, b: number): number => a * b; console.log(add(2, 3)); // 5 console.log(multiply(2, 3)); // 6
No output yet. Use console.log() to print values here.
Optional and Default Parameters
A parameter suffixed with `?`, like `discount?: number`, is optional - callers may omit it, and inside the function its type includes `undefined`. A default parameter, written `discount: number = 0`, is also optional to callers, but instead of becoming `undefined` when omitted, it falls back to the given default value, and its type does not need to include `undefined` at all.
Optional and default parameters must come after all required parameters in the parameter list - TypeScript (matching JavaScript) does not support required parameters after optional ones.
Try it yourself
function createUser(name: string, age?: number): string { return age !== undefined ? `${name} (${age})` : name; } function applyDiscount(price: number, percent: number = 10): number { return price - (price * percent) / 100; } console.log(createUser('Ada')); // 'Ada' console.log(createUser('Ada', 37)); // 'Ada (37)' console.log(applyDiscount(100)); // 90 - uses the default of 10 console.log(applyDiscount(100, 25)); // 75
No output yet. Use console.log() to print values here.
Function Overloads
Function overloads let a single function name accept several distinct call signatures, where the relationship between a specific input type and output type could not be expressed with a single signature using unions alone. You write several overload signatures followed by one general implementation signature that is compatible with all of them - only the overload signatures are visible to callers, and the implementation signature itself is not directly callable from outside.
Try it yourself
Overloads narrowing the return type based on the argumentfunction parseValue(value: string): string[]; function parseValue(value: number): number; function parseValue(value: string | number): string[] | number { if (typeof value === 'string') { return value.split(','); } return value * 2; } const parts = parseValue('a,b,c'); // typed as string[] const doubled = parseValue(21); // typed as number
No output yet. Use console.log() to print values here.
Typing Arrow Functions and Callbacks
An arrow function's type can be written as a standalone type alias, such as `type Comparator = (a: number, b: number) => number`, which is especially useful for describing a callback parameter accepted by another function (like a custom `sort` helper). This keeps the shape of the expected callback explicit and reusable, rather than repeating an inline function type at every call site.
Try it yourself
type Comparator = (a: number, b: number) => number; function customSort(items: number[], compare: Comparator): number[] { return [...items].sort(compare); } const ascending: Comparator = (a, b) => a - b; console.log(customSort([3, 1, 2], ascending)); // [1, 2, 3]
No output yet. Use console.log() to print values here.
Coding challenge
Write a function formatName(first: string, last: string, middle?: string): string that joins the parts with spaces, only including the middle name if it was provided. Call it twice, with and without a middle name.
// Write your solution here
No output yet. Use console.log() to print values here.
Answer every question below to mark this chapter as complete.
Quiz Yourself
Score: 0/51. Why is it good practice to explicitly annotate the return type of an exported function, even though TypeScript could infer it?
2. How does a default parameter (`percent: number = 10`) differ from an optional parameter (`percent?: number`) from inside the function body?
3. In a function with several overload signatures, which signature is directly callable from outside the function?
4. What ordering rule applies to optional and default parameters relative to required ones?
5. What is the benefit of writing a standalone type alias like `type Comparator = (a: number, b: number) => number` for a callback parameter?