Suggest an editImprove this articleRefine the answer for “Function overloads in TypeScript”. Your changes go to moderation before they’re published.Approval requiredContentWhat you’re changing🇺🇸EN🇺🇦UAPreviewTitle (EN)Short answer (EN)**Function overloads** - multiple signatures for one TypeScript function where the compiler picks the right return type based on arguments passed. ```typescript function parse(input: string): object; function parse(input: object): string; function parse(input: string | object): string | object { return typeof input === 'string' ? JSON.parse(input) : JSON.stringify(input); } parse('{"name":"Alice"}'); // object parse({ name: 'Alice' }); // string ``` **Key:** Overload signatures define the public API. The implementation signature is hidden from callers.Shown above the full answer for quick recall.Answer (EN)Image**Function overloads** in TypeScript let you declare multiple signatures for a single function, where the compiler picks the matching one at the call site based on the arguments you pass. ## Theory ### TL;DR - Think of it like a menu with two options for the same dish: pass one argument and get one return type; pass two and get another. TypeScript selects automatically. - Main difference from optional params: each overload enforces exact types for its call pattern, no `any` fallback at the call site. - The implementation signature is hidden from callers. They only see the overload signatures. - Order matters: specific signatures go first, broad ones last. - Zero runtime cost - overloads erase to a single JS function after compilation. ### Quick example ```typescript function greet(name: string): string; // Overload 1 function greet(first: string, last: string): string; // Overload 2 function greet(first: string, last?: string): string { // Implementation (not visible to callers) return last ? `Hello, ${first} ${last}!` : `Hello, ${first}!`; } greet("Alice"); // string - picks overload 1 greet("Bob", "Lee"); // string - picks overload 2 // greet(42); // Error: No overload matches this call ``` The two overload signatures define what callers can do. The implementation below them handles both cases with an optional parameter. TypeScript never exposes the implementation signature in autocomplete or error messages. ### Key difference Overloads give you a precise return type per argument shape at compile time. A single implementation with union types forces callers to handle `string | number` as the return even when they pass a specific literal argument. With overloads, passing `'name'` to `getValue` returns `string`; passing `'age'` returns `number`. No extra type guards needed on the calling side. ### When to use - Return type varies by argument value: overloads (e.g., `getValue('name')` returns `string`, `getValue('age')` returns `number`) - Argument count changes the shape of the result: overloads over optionals - You are writing a library or public API where callers need exact types - One function handles two clearly different call patterns (GET with no body vs POST with body) - All return types are the same and args are additive: union with optionals is simpler and cleaner ### How TypeScript resolves overloads The compiler scans overload signatures top-to-bottom and assigns the first match's return type to the call. That happens at compile time, not runtime. No type information survives into the emitted JS. In VS Code or any LSP-based editor, hovering a call site shows the resolved overload, not the implementation signature. ### Common mistakes **Putting the broad signature first** ```typescript // Wrong: compiler picks the first match - specific ones become unreachable function get(key: string): string | number; function get(key: 'age'): number; // Never reached // Correct function get(key: 'age'): number; function get(key: string): string | number; function get(key: string): string | number { /* ... */ } ``` If `string` comes before `'age'`, every call matches the broad signature and the literal overload is dead code. This is the most common overload bug on Stack Overflow. **Assuming overloads add runtime safety** ```typescript function safeDiv(a: number, b: number): number; function safeDiv(a: number): number; function safeDiv(a: number, b?: number): number { return a / b; // b can be undefined at runtime - result is NaN } safeDiv(10); // TypeScript is fine, runtime returns NaN ``` Overloads are compile-time only. Guard inside the implementation body. **Leaking optional params into overload signatures** ```typescript // Compiles, but the second overload misleads callers function greet(name: string): string; function greet(first: string, last?: string): string { /* ... */ } // optional in overload ``` Overload signatures should use required params only. Optional params belong in the implementation. **Overloading when a union is enough** ```typescript // Return type is always string - overloads add nothing here function format(value: string): string; function format(value: number): string; function format(value: string | number): string { return String(value); } // Just write: function format(value: string | number): string { return String(value); } ``` ### Real-world usage - React: `React.createElement` overloads by tag type to return different JSX shapes - Node.js: `fs.readFile` overloads callback vs Promise variants - Express: `res.json()` overloads object vs string input - Lodash: `_.get()` overloads path as `string | string[] | number` - React hooks: `useState<string>('')` resolves to `[string, Dispatch<SetStateAction<string>>]` through generic overload matching From experience: the real value of overloads is often IDE ergonomics. Hovering a call site and seeing exactly `Promise<string>` instead of `Promise<string | number>` makes intent clear to anyone reading the code months later. ### Follow-up questions **Q:** How does overload resolution handle ambiguous calls? **A:** The compiler picks the first matching signature in declaration order. If no overload matches, it is a compile error. There is no "best match" search - it stops at the first fit. **Q:** What is the difference between overloads and intersection types for function parameters? **A:** Overloads branch on argument shape: different args get different return types. Intersection types combine requirements: both constraints must be satisfied simultaneously. Use overloads when the function behaves differently depending on inputs. **Q:** Can overloads be generic? **A:** Yes. `function id<T>(x: T): T;` is a valid overload signature. Generic overloads are common in React hooks: `useState<string>('')` resolves through a generic overload to give you the exact `[string, Dispatch<SetStateAction<string>>]` tuple. **Q:** Do overloads affect bundle size or runtime performance? **A:** No. TypeScript erases all overload signatures during compilation. The emitted JS contains only the implementation function. **Q:** (Senior) A caller passes a union type as the argument. Which overload does TypeScript pick? **A:** TypeScript does not automatically split a union into separate overload lookups. If you pass `key: string | 'age'`, the compiler checks that union as a whole against each signature and most likely resolves to the broad overload's return type. This is a common source of confusion when consuming overloaded APIs with derived types. ## Examples ### HTTP request wrapper Typical pattern in Express apps and API clients: typed handlers where GET takes no body and POST requires one. ```typescript function request(method: 'GET', url: string): Promise<string>; function request(method: 'POST', url: string, body: object): Promise<number>; function request(method: string, url: string, body?: object): Promise<string | number> { if (method === 'GET') return Promise.resolve(`Fetched ${url}`); return Promise.resolve(Object.keys(body!).length); } const response = request('GET', '/users'); // Promise<string> const status = request('POST', '/users', {id: 1}); // Promise<number> // request('POST', '/users'); // Error: body is required for POST ``` TypeScript forces you to provide `body` on POST calls and blocks it on GET calls. The implementation handles both with an optional param, but callers never see that detail. ### Dynamic return type based on key A common interview pattern: a function that returns different types depending on a literal string argument. ```typescript function getValue(key: 'name'): string; function getValue(key: 'age'): number; function getValue(key: 'active'): boolean; function getValue(key: string): string | number | boolean { const data = { name: 'Alice', age: 30, active: true }; return data[key as keyof typeof data]; } const name = getValue('name'); // string const age = getValue('age'); // number const active = getValue('active'); // boolean ``` Without overloads, all three calls return `string | number | boolean` and you end up writing type guards everywhere the result is used. ### Array extraction with optional count A generic overload showing how argument count changes the return shape. ```typescript function getFirst<T>(arr: T[]): T | undefined; function getFirst<T>(arr: T[], count: number): T[]; function getFirst<T>(arr: T[], count?: number): T | T[] | undefined { return count === undefined ? arr[0] : arr.slice(0, count); } const users = ['Alice', 'Bob', 'Carol']; const one = getFirst(users); // string | undefined const three = getFirst(users, 2); // string[] ``` The two overloads say: without a count you might get nothing (empty array case), with a count you always get an array. A single signature with `count?: number` would return `string | string[] | undefined` for both calls, which forces unnecessary checks on the caller side.For the reviewerNote to the moderator (optional)Visible only to the moderator. Helps review go faster.