Functions are where TypeScript's type system delivers the most value. Every parameter, return value, and callback can be typed — giving you autocomplete, error checking, and documentation for free.
Parameter and Return Types
function multiply(a: number, b: number): number {
return a * b;
}
const result = multiply(3, 4); // 12The : number after each parameter is the type annotation. The
: number after the parentheses is the return type.
You can often omit the return type — TypeScript infers it:
// Return type inferred as number
function multiply(a: number, b: number) {
return a * b;
}Best practice: always annotate return types on public functions. They serve as documentation and prevent accidental return type changes.
Optional and Default Parameters
// Optional parameter — must come after required ones
function greet(name: string, greeting?: string): string {
return `${greeting ?? "Hello"}, ${name}!`;
}
greet("Sita"); // "Hello, Sita!"
greet("Sita", "Namaste"); // "Namaste, Sita!"
// Default parameter
function createUser(name: string, role: string = "student") {
return { name, role };
}
createUser("Ram"); // { name: "Ram", role: "student" }
createUser("Sita", "admin"); // { name: "Sita", role: "admin" }Rest Parameters
Collect any number of arguments into an array:
function sum(...numbers: number[]): number {
return numbers.reduce((total, n) => total + n, 0);
}
sum(1, 2, 3, 4); // 10
sum(10, 20, 30); // 60Named Arguments (Destructuring)
For functions with many parameters, destructure an object:
interface CreateUserOptions {
name: string;
age: number;
role?: string;
active?: boolean;
}
function createUser({ name, age, role = "student", active = true }: CreateUserOptions) {
return { name, age, role, active };
}
createUser({ name: "Sita", age: 21 });
createUser({ name: "Ram", age: 22, role: "admin", active: false });Function Overloads
When a function accepts different argument shapes and returns different types:
function format(input: string): string;
function format(input: number): string;
function format(input: Date): string;
function format(input: string | number | Date): string {
if (typeof input === "string") return input.trim();
if (typeof input === "number") return input.toFixed(2);
return input.toISOString().split("T")[0];
}
format(" hello "); // "hello"
format(3.14159); // "3.14"
format(new Date()); // "2026-08-23"Arrow Functions
const double = (n: number): number => n * 2;
const filter = <T>(items: T[], predicate: (item: T) => boolean): T[] =>
items.filter(predicate);
// Usage
const evens = filter([1, 2, 3, 4, 5], (n) => n % 2 === 0);
// [2, 4]Callback Types
When passing functions as parameters:
type FetchCallback = (error: Error | null, data?: string) => void;
function fetchData(url: string, callback: FetchCallback): void {
// ... async work
callback(null, "result");
}Pure vs Side-Effect Functions
// Pure — same input always gives same output, no side effects
function add(a: number, b: number): number {
return a + b;
}
// Side effect — modifies external state
let count = 0;
function increment(): void {
count++;
}Prefer pure functions — they are easier to test, debug and reason about.
Tips
- Always type callback parameters — this is where most bugs hide.
- Use
neverfor functions that throw or loop forever. - Use default parameters over optional parameters when the default makes the call site clearer.
- Destructure objects in function parameters when there are 3+ args.
Next: classes — object-oriented TypeScript.