Function Type Annotations
// Basic function with types
function add(a: number, b: number): number {
return a + b;
}
// Arrow function with types
const multiply = (a: number, b: number): number => {
return a * b;
};
// Return type inference
function subtract(a: number, b: number) {
return a - b; // Return type inferred as number
}
// Void return type
function logMessage(message: string): void {
console.log(message);
}
// Never return type (throws or infinite loop)
function fail(message: string): never {
throw new Error(message);
}
Optional and Default Parameters
// Optional parameter (must come after required)
function greet(name: string, greeting?: string): string {
return `${greeting || "Hello"}, ${name}!`;
}
greet("Alice"); // "Hello, Alice!"
greet("Bob", "Hi"); // "Hi, Bob!"
// Default parameter
function greetWithDefault(name: string, greeting: string = "Hello"): string {
return `${greeting}, ${name}!`;
}
greetWithDefault("Alice"); // "Hello, Alice!"
greetWithDefault("Bob", "Hi"); // "Hi, Bob!"
// Default with type inference
function createUser(name: string, role = "user") {
// role is inferred as string
return { name, role };
}
Rest Parameters
// Rest parameters
function sum(...numbers: number[]): number {
return numbers.reduce((acc, num) => acc + num, 0);
}
sum(1, 2, 3); // 6
sum(1, 2, 3, 4, 5); // 15
// Rest with other parameters
function buildName(firstName: string, ...restOfName: string[]): string {
return firstName + " " + restOfName.join(" ");
}
buildName("John", "Paul", "Jones"); // "John Paul Jones"
// Typed rest parameters with tuples
function readButtonInput(...args: [string, number, ...boolean[]]): void {
const [name, version, ...flags] = args;
console.log(name, version, flags);
}
Function Type Expressions
// Function type expression
type MathOperation = (a: number, b: number) => number;
const add: MathOperation = (a, b) => a + b;
const subtract: MathOperation = (a, b) => a - b;
// Using in other types
interface Calculator {
add: MathOperation;
subtract: MathOperation;
multiply: MathOperation;
divide: MathOperation;
}
// Callback function types
type Callback = (error: Error | null, result?: string) => void;
function fetchData(url: string, callback: Callback): void {
// ...
callback(null, "data");
}
// Array methods with typed callbacks
const numbers = [1, 2, 3, 4, 5];
const doubled = numbers.map((n: number): number => n * 2);
// Type alias for event handler
type ClickHandler = (event: MouseEvent) => void;
Generic Functions
// Basic generic function
function identity<T>(arg: T): T {
return arg;
}
identity<string>("hello"); // explicit type
identity(42); // inferred as number
// Generic with constraints
function getLength<T extends { length: number }>(arg: T): number {
return arg.length;
}
getLength("hello"); // 5
getLength([1, 2, 3]); // 3
// getLength(123); // Error: number doesn't have length
// Multiple type parameters
function pair<T, U>(first: T, second: U): [T, U] {
return [first, second];
}
const result = pair("hello", 42); // [string, number]
// Generic with default type
function createArray<T = string>(length: number, value: T): T[] {
return Array(length).fill(value);
}
createArray(3, "x"); // string[] (explicit)
createArray(3, 42); // number[] (inferred)
createArray<>(3, "x"); // string[] (default)
Function Overloads
// Function overloads
function makeDate(timestamp: number): Date;
function makeDate(year: number, month: number, day: number): Date;
function makeDate(yearOrTimestamp: number, month?: number, day?: number): Date {
if (month !== undefined && day !== undefined) {
return new Date(yearOrTimestamp, month - 1, day);
}
return new Date(yearOrTimestamp);
}
makeDate(1609459200000); // From timestamp
makeDate(2024, 1, 15); // From year, month, day
// makeDate(2024, 1); // Error: no matching overload
// Overloads with different return types
function getValue(key: "name"): string;
function getValue(key: "age"): number;
function getValue(key: "active"): boolean;
function getValue(key: string): string | number | boolean {
const data: Record<string, string | number | boolean> = {
name: "Alice",
age: 30,
active: true
};
return data[key];
}
const name = getValue("name"); // string
const age = getValue("age"); // number
const active = getValue("active"); // boolean
this Parameter
// Explicit this type
interface User {
name: string;
greet(this: User): string;
}
const user: User = {
name: "Alice",
greet() {
return `Hello, I'm ${this.name}`;
}
};
user.greet(); // "Hello, I'm Alice"
// const callback = user.greet;
// callback(); // Error: 'this' context of type 'void'
// this in class methods
class Counter {
count = 0;
// Arrow function preserves 'this'
increment = () => {
this.count++;
};
// Regular method needs binding
decrement() {
this.count--;
}
}
Practical Examples
// API fetch function
async function fetchUser(id: number): Promise<User> {
const response = await fetch(`/api/users/${id}`);
return response.json();
}
// Event handler
function handleClick(event: React.MouseEvent<HTMLButtonElement>): void {
console.log("Clicked:", event.currentTarget);
}
// Debounce utility
function debounce<T extends (...args: any[]) => any>(
func: T,
wait: number
): (...args: Parameters<T>) => void {
let timeoutId: ReturnType<typeof setTimeout>;
return (...args) => {
clearTimeout(timeoutId);
timeoutId = setTimeout(() => func(...args), wait);
};
}
// Higher-order function
function withLogging<T extends (...args: any[]) => any>(fn: T): T {
return ((...args) => {
console.log("Calling with:", args);
const result = fn(...args);
console.log("Result:", result);
return result;
}) as T;
}
Key Takeaways
- • Type both parameters and return values
- • Use
? for optional parameters
- • Use rest parameters with typed arrays
- • Generic functions enable type-safe reusability
- • Function overloads handle multiple signatures
- • Be explicit with
this when needed