函数重载允许为同一个函数提供多个类型签名,根据传入参数的类型和数量,TypeScript 会选择合适的签名进行类型检查。这让函数的调用方式更加明确,IDE 也能提供更好的提示。
函数重载由重载签名和实现签名组成。重载签名描述函数的不同调用方式,实现签名包含实际的逻辑代码。
function format(input: string): string
function format(input: number): string
function format(input: string | number): string {
if (typeof input === "string") {
return `字符串: ${input}`
}
return `数字: ${input}`
}
console.log(format("hello"))
console.log(format(123))
前两行是重载签名,第三行是实现签名。调用时只能使用重载签名中定义的方式。
实现签名必须兼容所有重载签名,参数类型使用联合类型,返回值类型也要兼容。
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(1234567890000)
const d2 = makeDate(2024, 1, 1)
实现签名的参数使用可选参数,以兼容两种调用方式。
不同的重载签名可以有不同的返回值类型。
function parse(input: string): string
function parse(input: number): number
function parse(input: string | number): string | number {
return input
}
const str = parse("hello")
const num = parse(123)
console.log(typeof str)
console.log(typeof num)
TypeScript 会根据传入参数的类型推断返回值的类型,这是重载的一个重要用途。
重载可以描述参数数量不同的调用方式。
function createElement(tag: "a"): HTMLAnchorElement
function createElement(tag: "canvas"): HTMLCanvasElement
function createElement(tag: "table"): HTMLTableElement
function createElement(tag: string): HTMLElement
function createElement(tag: string): HTMLElement {
return document.createElement(tag)
}
const anchor = createElement("a")
const canvas = createElement("canvas")
const div = createElement("div")
不同的标签返回不同的元素类型,让类型系统更加精确。
有些情况下,联合类型比重载更简单。
function format1(input: string | number): string {
if (typeof input === "string") {
return `字符串: ${input}`
}
return `数字: ${input}`
}
function format2(input: string): string
function format2(input: number): string
function format2(input: string | number): string {
if (typeof input === "string") {
return `字符串: ${input}`
}
return `数字: ${input}`
}
两种方式都能实现类似的效果。重载的优势在于可以更精确地描述不同参数类型对应的返回值类型。
类中的方法也可以重载。
class 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()
console.log(calc.add(1, 2))
console.log(calc.add("hello", "world"))
方法重载让类的接口更加清晰,调用者可以根据参数类型知道返回值类型。
重载签名按照定义的顺序匹配,更具体的签名应该放在前面。
function process(value: string): string
function process(value: any): any
function process(value: any): any {
return value
}
const result1 = process("hello")
const result2 = process(123)
如果先定义 any 类型的签名,后面的签名永远不会被匹配。所以要把更具体的签名放在前面。
重载签名中不使用可选参数语法,而是为每种情况定义单独的签名。
function fetch(url: string): Promise<any>
function fetch(url: string, options: RequestInit): Promise<any>
function fetch(url: string, options?: RequestInit): Promise<any> {
console.log(`请求 ${url}`)
if (options) {
console.log(`选项:`, options)
}
return Promise.resolve({ data: "响应数据" })
}
const result1 = fetch("https://api.example.com/data")
const result2 = fetch("https://api.example.com/data", { method: "POST" })
实现签名使用可选参数,重载签名分别描述有无 options 参数的情况。
函数重载在实际开发中常用于工具函数和库函数。
interface QueryResult {
id: number
name: string
}
function query(sql: string): QueryResult[]
function query(sql: string, params: any[]): QueryResult[]
function query(sql: string, params?: any[]): QueryResult[] {
console.log(`执行 SQL: ${sql}`)
if (params) {
console.log(`参数:`, params)
}
return [{ id: 1, name: "张三" }]
}
const result1 = query("SELECT * FROM users")
const result2 = query("SELECT * FROM users WHERE id = ?", [1])
console.log(result1)
console.log(result2)
query 函数有两种调用方式,重载签名清晰地描述了这两种方式。
函数重载为同一个函数提供多个类型签名,让函数的调用方式更加明确。重载签名描述不同的调用方式,实现签名包含实际逻辑。重载的优势在于可以精确描述参数类型与返回值类型的对应关系,提供更好的类型检查和 IDE 提示。合理使用重载可以让函数接口更加清晰易用。