TypeScript函数重载 #

一、函数重载基础 #

1.1 什么是函数重载 #

函数重载允许一个函数根据不同的参数类型或数量,有不同的行为和返回类型。

typescript
function add(a: number, b: number): number;
function add(a: string, b: string): string;
function add(a: any, b: any): any {
    return a + b;
}

const num = add(1, 2);
const str = add('hello', 'world');

1.2 重载签名和实现签名 #

typescript
function add(a: number, b: number): number;
function add(a: string, b: string): string;
function add(a: any, b: any): any {
    return a + b;
}
  • 前两行是重载签名(overload signatures)
  • 最后一行是实现签名(implementation signature)

二、基本重载 #

2.1 不同参数类型 #

typescript
function format(value: number): string;
function format(value: string): string;
function format(value: number | string): string {
    if (typeof value === 'number') {
        return value.toFixed(2);
    }
    return value.toUpperCase();
}

console.log(format(3.14159));
console.log(format('hello'));

2.2 不同参数数量 #

typescript
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);
}

const d1 = makeDate(1234567890);
const d2 = makeDate(2024, 1, 1);

2.3 不同返回类型 #

typescript
function parse(value: string): string;
function parse(value: string, radix: number): number;
function parse(value: string, radix?: number): string | number {
    if (radix !== undefined) {
        return parseInt(value, radix);
    }
    return value;
}

const str = parse('123');
const num = parse('123', 10);

三、重载规则 #

3.1 实现签名必须兼容所有重载 #

typescript
function fn(x: string): string;
function fn(x: number): number;
function fn(x: boolean): boolean;
function fn(x: string | number | boolean): string | number | boolean {
    return x;
}

3.2 重载签名不能有实现 #

typescript
function add(a: number, b: number): number {
    return a + b;
}
function add(a: string, b: string): string {
    return a + b;
}

function add(a: number, b: number): number;
function add(a: string, b: string): string;
function add(a: any, b: any): any {
    return a + b;
}

3.3 调用时只使用重载签名 #

typescript
function fn(x: string): string;
function fn(x: number): number;
function fn(x: string | number): string | number {
    return x;
}

fn('hello').toUpperCase();
fn(123).toFixed(2);
fn(true);

四、实际应用 #

4.1 DOM操作 #

typescript
function createElement(tag: 'a'): HTMLAnchorElement;
function createElement(tag: 'canvas'): HTMLCanvasElement;
function createElement(tag: 'table'): HTMLTableElement;
function createElement(tag: string): HTMLElement {
    return document.createElement(tag);
}

const anchor = createElement('a');
const canvas = createElement('canvas');
const table = createElement('table');
const div = createElement('div');

4.2 事件处理 #

typescript
function on(element: HTMLElement, event: 'click', handler: (e: MouseEvent) => void): void;
function on(element: HTMLElement, event: 'keydown', handler: (e: KeyboardEvent) => void): void;
function on(element: HTMLElement, event: string, handler: (e: Event) => void): void {
    element.addEventListener(event, handler);
}

on(document.body, 'click', e => {
    console.log(e.clientX);
});

on(document.body, 'keydown', e => {
    console.log(e.key);
});

4.3 API请求 #

typescript
interface User {
    id: number;
    name: string;
}

function fetchUser(): User[];
function fetchUser(id: number): User;
function fetchUser(id?: number): User | User[] {
    if (id === undefined) {
        return [
            { id: 1, name: 'Alice' },
            { id: 2, name: 'Bob' }
        ];
    }
    return { id, name: 'User' };
}

const users = fetchUser();
const user = fetchUser(1);

4.4 工具函数 #

typescript
function get(obj: object, key: string): any;
function get<T>(obj: T, key: keyof T): T[keyof T];
function get(obj: any, key: string): any {
    return obj[key];
}

const user = { name: 'Alice', age: 25 };
const name = get(user, 'name');
const age = get(user, 'age');

五、重载与联合类型 #

5.1 何时使用重载 #

typescript
function len(s: string): number;
function len(arr: any[]): number;
function len(x: any): number {
    return x.length;
}

function len(x: string | any[]): number {
    return x.length;
}

5.2 重载的优势 #

typescript
function createElement(tag: 'a'): HTMLAnchorElement;
function createElement(tag: 'canvas'): HTMLCanvasElement;
function createElement(tag: string): HTMLElement;

function createElement(tag: string): HTMLElement {
    return document.createElement(tag);
}

5.3 选择建议 #

场景 推荐
返回类型不同 函数重载
参数类型不同且返回类型不同 函数重载
参数类型不同但返回类型相同 联合类型
需要精确的类型推断 函数重载

六、高级重载 #

6.1 泛型重载 #

typescript
function identity<T>(arg: T): T;
function identity<T extends string>(arg: T): T;
function identity<T>(arg: T): T {
    return arg;
}

const str = identity('hello');
const num = identity(123);

6.2 条件返回类型 #

typescript
function process<T extends string | number>(
    value: T
): T extends string ? string : number {
    if (typeof value === 'string') {
        return value.toUpperCase() as any;
    }
    return value * 2 as any;
}

const str = process('hello');
const num = process(5);

6.3 构造函数重载 #

typescript
interface Animal {
    name: string;
}

interface AnimalConstructor {
    new (name: string): Animal;
    (name: string): Animal;
}

function createAnimal(ctor: AnimalConstructor, name: string): Animal {
    return new ctor(name);
}

七、常见错误 #

7.1 实现签名过于宽泛 #

typescript
function fn(x: string): string;
function fn(x: number): number;
function fn(x: any): any {
    return x;
}

fn(true);

7.2 重载顺序错误 #

typescript
function fn(x: any): any;
function fn(x: string): string;
function fn(x: number): number;
function fn(x: any): any {
    return x;
}

function fn(x: string): string;
function fn(x: number): number;
function fn(x: any): any {
    return x;
}

7.3 可选参数处理 #

typescript
function fn(x: number): number;
function fn(x: number, y: number): number;
function fn(x: number, y?: number): number {
    return y !== undefined ? x + y : x;
}

八、实用示例 #

8.1 类型安全的Object.keys #

typescript
function keys<T extends object>(obj: T): (keyof T)[];
function keys(obj: object): string[];
function keys(obj: object): string[] {
    return Object.keys(obj);
}

const user = { name: 'Alice', age: 25 };
const userKeys = keys(user);

8.2 类型安全的assign #

typescript
function assign<T, U>(target: T, source: U): T & U;
function assign<T, U, V>(target: T, source1: U, source2: V): T & U & V;
function assign(target: any, ...sources: any[]): any {
    return Object.assign(target, ...sources);
}

const result = assign({ a: 1 }, { b: 2 });
const result2 = assign({ a: 1 }, { b: 2 }, { c: 3 });

8.3 类型安全的querySelector #

typescript
function querySelector(selector: 'div'): HTMLDivElement;
function querySelector(selector: 'a'): HTMLAnchorElement;
function querySelector(selector: string): HTMLElement | null;
function querySelector(selector: string): HTMLElement | null {
    return document.querySelector(selector);
}

const div = querySelector('div');
const anchor = querySelector('a');
const span = querySelector('span');

九、总结 #

本章介绍了TypeScript函数重载:

重载要点 #

  1. 重载签名定义函数的多种调用方式
  2. 实现签名必须兼容所有重载
  3. 调用时只使用重载签名
  4. 重载顺序很重要

使用场景 #

  1. 不同参数类型返回不同类型
  2. 不同参数数量返回不同类型
  3. 需要精确的类型推断
  4. API设计提供更好的类型提示

最佳实践 #

  1. 重载签名从具体到宽泛排序
  2. 实现签名处理所有情况
  3. 优先考虑联合类型
  4. 避免过于复杂的重载
最后更新:2026-03-26