After this lesson, you will be able to: Type functions: parameters, return types, optional + default, overloads.
Function types are where TS really pays off, function signatures become living contracts.
Inferred vs explicit.
// Inferred returnfunction add(a: number, b: number) { return a + b; } // returns number// Explicit returnfunction greet(name: string): string { return `Hello, ${name}`; }// Asyncasync function fetchUser(id: string): Promise<User> {const r = await fetch(`/users/${id}`);return r.json();}// Arrow + type annotationconst sub = (a: number, b: number): number => a - b;// Function type aliastype Handler<T> = (event: T) => void;const onClick: Handler<MouseEvent> = (e) => console.log(e.clientX);
Match runtime behavior in types.
// Optional parameterfunction greet(name: string, greeting?: string) {return `${greeting ?? "Hello"}, ${name}`;}// Default value (becomes optional)function greet2(name: string, greeting = "Hello") {return `${greeting}, ${name}`;}// Restfunction sum(...nums: number[]): number {return nums.reduce((a, b) => a + b, 0);}// Keyword args via options object (preferred for >2 args)function createUser(opts: { name: string; email: string; admin?: boolean }) {return { ...opts, id: crypto.randomUUID() };}
Use sparingly, generics + union returns are usually cleaner.
function pick(arr: string[], n: number): string;function pick(arr: number[], n: number): number;function pick(arr: (string | number)[], n: number): string | number {return arr[n];}const a = pick(["a", "b"], 0); // stringconst b = pick([1, 2], 0); // number// Often better: genericfunction pickG<T>(arr: T[], n: number): T { return arr[n]; }
Using `Function` type. Way too broad. Type the exact signature. Letting TS infer when an explicit annotation reads better (for exported APIs especially). Returning Promise<any>. Type the result; downstream loses guarantees. Long parameter lists. Use options objects with destructuring.
Pick the cleanest answer.
Sign in and purchase access to unlock this lesson.