Designs complex generic types, refactors `any` types to strict alternatives, creates type guards and utility types, and resolves TypeScript compiler errors. Use when the user asks about TypeScript (TS) types, generics, type inference, type guards, removing `any` types, strict typing, type errors, `infer`, `extends`, conditional types, mapped types, template literal types, branded/opaque types, or utility types like `Partial`, `Record`, `ReturnType`, and `Awaited`.
97
97%
Does it follow best practices?
Impact
Pending
No eval scenarios have been run
Passed
No known issues
Function overloads allow you to define multiple function signatures for a single function implementation. TypeScript selects the appropriate signature based on the arguments provided.
// Overload signatures (what callers see)
function greet(name: string): string;
function greet(firstName: string, lastName: string): string;
// Implementation signature (must be compatible with all overloads)
function greet(nameOrFirst: string, lastName?: string): string {
if (lastName) {
return `Hello, ${nameOrFirst} ${lastName}!`;
}
return `Hello, ${nameOrFirst}!`;
}
// Usage - TypeScript picks the right overload
greet("Alice"); // Uses first overload
greet("Alice", "Smith"); // Uses second overloadTypeScript tries overloads in order from top to bottom, using the first that matches:
// Order matters! More specific overloads should come first
function processValue(value: string): string;
function processValue(value: number): number;
function processValue(value: string | number): string | number {
if (typeof value === "string") {
return value.toUpperCase();
}
return value * 2;
}
const str = processValue("hello"); // Type: string
const num = processValue(42); // Type: numberThe DOM's querySelector uses overloads for element type inference:
// Simplified version of what lib.dom.d.ts defines
interface Document {
// Specific overload for known HTML elements
querySelector<K extends keyof HTMLElementTagNameMap>(
selectors: K
): HTMLElementTagNameMap[K] | null;
// Fallback for custom selectors
querySelector(selectors: string): Element | null;
}
const body = document.querySelector("body"); // Type: HTMLBodyElement | null
const custom = document.querySelector(".my-class"); // Type: Element | nullWhen wrapping a function, mirror its overloads to preserve type inference:
// Problem: Simple wrapper loses overload behavior
export function nonNullQuerySelector(tag: string) {
const element = document.querySelector(tag);
if (!element) {
throw new Error(`Element not found: ${tag}`);
}
return element;
}
const body = nonNullQuerySelector("body"); // Type: Element (lost HTMLBodyElement!)
// Solution: Add overload that mirrors querySelector
export function nonNullQuerySelector<K extends keyof HTMLElementTagNameMap>(
tag: K
): HTMLElementTagNameMap[K];
export function nonNullQuerySelector(tag: string): Element;
export function nonNullQuerySelector(tag: string): Element {
const element = document.querySelector(tag);
if (!element) {
throw new Error(`Element not found: ${tag}`);
}
return element;
}
const body = nonNullQuerySelector("body"); // Type: HTMLBodyElement
const custom = nonNullQuerySelector(".custom"); // Type: Elementclass Calculator {
add(a: number, b: number): number;
add(a: string, b: string): string;
add(a: number | string, b: number | string): number | string {
if (typeof a === "number" && typeof b === "number") {
return a + b;
}
return String(a) + String(b);
}
}
const calc = new Calculator();
const sum = calc.add(1, 2); // Type: number
const concat = calc.add("hello", "world"); // Type: stringinterface StringOrNumberFunc {
(value: string): string;
(value: number): number;
}
const process: StringOrNumberFunc = (value: string | number) => {
if (typeof value === "string") {
return value.toUpperCase();
}
return value * 2;
};A common pattern for event systems:
interface EventMap {
click: MouseEvent;
keydown: KeyboardEvent;
submit: SubmitEvent;
}
interface EventEmitter {
// Specific overload for known events
on<K extends keyof EventMap>(
event: K,
handler: (e: EventMap[K]) => void
): void;
// Fallback for custom events
on(event: string, handler: (e: Event) => void): void;
}
const emitter: EventEmitter = {
on(event: string, handler: (e: any) => void) {
// Implementation
},
};
// Handler type is correctly inferred
emitter.on("click", (e) => {
console.log(e.clientX); // e is MouseEvent
});
emitter.on("keydown", (e) => {
console.log(e.key); // e is KeyboardEvent
});
emitter.on("custom", (e) => {
// e is Event (fallback)
});Sometimes a union type is simpler than overloads:
// Overloads - when return type depends on input type
function parse(input: string): object;
function parse(input: object): string;
function parse(input: string | object): string | object {
if (typeof input === "string") {
return JSON.parse(input);
}
return JSON.stringify(input);
}
// Union - when return type is always the same
function process(input: string | number): string {
return String(input);
}function createElement(tag: "input"): HTMLInputElement;
function createElement(tag: "button", text?: string): HTMLButtonElement;
function createElement(tag: string, text?: string): HTMLElement;
function createElement(tag: string, text?: string): HTMLElement {
const element = document.createElement(tag);
if (text) {
element.textContent = text;
}
return element;
}The implementation signature is NOT visible to callers:
function example(a: string): string;
function example(a: number): number;
function example(a: string | number): string | number {
return typeof a === "string" ? a.toUpperCase() : a * 2;
}
// Error: No overload matches this call
example(true); // Even though implementation accepts anyPut specific overloads before general ones:
// BAD - general overload catches everything
function bad(x: any): any;
function bad(x: string): string; // Never reached!
function bad(x: any): any {
return x;
}
// GOOD - specific overloads first
function good(x: string): string;
function good(x: any): any;
function good(x: any): any {
return x;
}The implementation signature must handle all overload cases:
function process(x: string): string;
function process(x: number): number;
// Error: Implementation signature must be compatible
function process(x: string): string {
return x.toUpperCase();
}
// Correct
function process(x: string | number): string | number {
if (typeof x === "string") {
return x.toUpperCase();
}
return x * 2;
}