Functions are where TypeScript delivers the most day-to-day value. By annotating parameters and return values, you turn every function signature into a contract that the compiler enforces and your editor understands. In this lesson you will learn how to type parameters, return values, optional and default parameters, rest parameters, and function expressions.
Annotations for parameters go after the parameter name; the return type goes after the parameter list:
function add(a: number, b: number): number {
return a + b;
}
add(2, 3); // 5
add("2", 3); // Error: string is not assignable to numberParameter annotations are essential — without them (and with noImplicitAny on) the compiler reports an error, because it has no way to know what callers should pass. Return types, on the other hand, are usually inferred correctly. Many teams still write them explicitly on exported functions: an explicit return type documents intent and catches accidental changes when someone edits the function body.
A ? marks a parameter as optional; a default value makes it optional and supplies a fallback:
function buildUrl(path: string, query?: string): string {
return query ? path + "?" + query : path;
}
function paginate(items: string[], pageSize: number = 10): string[] {
return items.slice(0, pageSize);
}
buildUrl("/lessons"); // OK, query is undefined
paginate(["a", "b", "c"]); // OK, pageSize defaults to 10Inside buildUrl, the type of query is string | undefined, so the compiler forces you to handle the missing case. Optional parameters must come after required ones.
Rest parameters collect any number of trailing arguments into a typed array:
function sum(...values: number[]): number {
return values.reduce((total, v) => total + v, 0);
}
sum(1, 2, 3, 4); // 10Arrow functions are typed the same way. You can also describe an entire function shape with a function type expression, which is invaluable for callbacks:
const double = (n: number): number => n * 2;
// A named function type
type Formatter = (value: number, currency: string) => string;
const formatPrice: Formatter = (value, currency) => {
return currency + value.toFixed(2);
};Notice that formatPrice needs no parameter annotations: because it is assigned to a variable of type Formatter, TypeScript infers value: number and currency: string from context. This is called contextual typing and it is why callbacks passed to map, filter, and event handlers rarely need annotations.
When your function accepts a callback, describe the callback inline:
function retry(times: number, task: () => Promise<boolean>): void {
// implementation omitted
}
function onEach(items: string[], visit: (item: string, index: number) => void) {
items.forEach(visit);
}Use void when a function returns nothing useful. Use never when it cannot return at all:
function log(message: string): void {
console.log(message);
}
function fail(message: string): never {
throw new Error(message);
}The distinction matters for control-flow analysis: after a call to a never-returning function, the compiler knows execution stops.
? makes a parameter optional and its type includes undefined; defaults supply a fallback value....values: number[].(n: number) => string describe callbacks and enable contextual typing.void means "returns nothing"; never means "never returns".Next lesson: Objects and Interfaces — describing the shape of structured data.