Type parameters and returns, optional, default, and rest parameters, describe callbacks with function types, and write overloads for multiple call shapes.
Why: annotate parameters always; return types are usually inferred. Optional (?), default, and rest parameters all carry types. Function type aliases describe callbacks.
// Parameter and return types
function add(a: number, b: number): number {
return a + b;
}
// Optional and default parameters
function greet(name: string, greeting = 'Hello', title?: string) {
return `${greeting}, ${title ? title + ' ' : ''}${name}!`;
}
// Rest parameters
function sum(...nums: number[]): number {
return nums.reduce((acc, n) => acc + n, 0);
}
// Function types — the shape of a callback
type Comparator = (a: number, b: number) => number;
const byValueDesc: Comparator = (a, b) => b - a;
console.log(add(2, 3)); // 5
console.log(greet('Alice')); // Hello, Alice!
console.log(greet('Bob', 'Hi', 'Dr.')); // Hi, Dr. Bob!
console.log(sum(1, 2, 3, 4)); // 10
console.log([3, 1, 2].sort(byValueDesc)); // [3, 2, 1]Why: overloads declare several call shapes for one function, so each call site gets precise types. Write the specific signatures first, then one implementation that handles them all.
// Overload signatures — what callers are allowed to write
function parseDate(value: string): Date;
function parseDate(timestamp: number): Date;
function parseDate(year: number, month: number, day: number): Date;
// One implementation covers every overload
function parseDate(a: string | number, month?: number, day?: number): Date {
if (typeof a === 'string') return new Date(a);
if (month !== undefined && day !== undefined) {
return new Date(a, month - 1, day);
}
return new Date(a);
}
console.log(parseDate('2026-06-11'));
console.log(parseDate(1750000000000));
console.log(parseDate(2026, 6, 11));
// parseDate(true); // Error: no overload matches this call
// parseDate(2026, 6); // Error: matches no overload either