函数签名与重载
2026/7/9大约 5 分钟
函数签名与重载
概述
Go 的函数系统简洁而严格 —— 每个函数只有一个签名,不支持重载。TypeScript 则提供了丰富的函数类型表达:函数类型表达式、可选参数、默认参数、Rest 参数、this 类型标注,以及函数重载。对于 Go 开发者来说,函数重载可能是最陌生的概念之一。
Go 开发者已知
// Go 函数语法
func add(a int, b int) int {
return a + b
}
// 类型简写
func sub(a, b int) int {
return a - b
}
// 多返回值
func div(a, b int) (int, error) {
if b == 0 {
return 0, errors.New("division by zero")
}
return a / b, nil
}
// 变参
func sum(nums ...int) int {
total := 0
for _, n := range nums {
total += n
}
return total
}
// Go 不支持函数重载 —— 必须不同名称
func addInt(a, b int) int { return a + b }
func addFloat(a, b float64) float64 { return a + b }Go 函数特点:
- 函数是一等公民,但类型系统表达能力有限
- 不支持函数重载(同名不同参数)
- 不支持默认参数
- 不支持可选参数
- 多返回值是 Go 的招牌特性,TS 中没有直接对应
TypeScript 怎么做
函数类型表达式
// 类型注解
function add(a: number, b: number): number {
return a + b
}
// 函数类型表达式(独立签名)
type BinaryOp = (a: number, b: number) => number
const multiply: BinaryOp = (x, y) => x * y
// 箭头函数
const divide = (a: number, b: number): number => a / b可选参数与默认参数
// 可选参数 ?
function greet(name: string, greeting?: string): string {
return `${greeting ?? 'Hello'}, ${name}!`
}
// 默认参数
function createUser(name: string, age: number = 18): { name: string; age: number } {
return { name, age }
}
// 可选参数必须在必选之后
// Error: 可选参数不能在必选之前
// function bad(opt?: string, req: string) {}Rest 参数
// Rest 参数(类似 Go 变参)
function sum(...nums: number[]): number {
return nums.reduce((acc, n) => acc + n, 0)
}
// 带类型的 Rest
function buildUrl(base: string, ...params: string[]): string {
return `${base}?${params.join('&')}`
}this 类型标注
// 明确指定 this 类型
interface User {
name: string
getName: (this: User) => string
}
const user: User = {
name: 'Alice',
getName() {
return this.name // this 被推断为 User
},
}
// 与回调中的 this
function onClick(this: HTMLButtonElement, event: MouseEvent) {
console.log(this.textContent) // this 类型为 HTMLButtonElement
}函数重载
// 重载签名(多个函数签名声明)
function process(input: string): string
function process(input: number): number
function process(input: boolean): boolean
// 实现签名(必须兼容所有重载)
function process(input: string | number | boolean): string | number | boolean {
if (typeof input === 'string') return input.toUpperCase()
if (typeof input === 'number') return input * 2
return !input
}
// 使用
process('hello') // 推断为 string
process(42) // 推断为 number
process(true) // 推断为 boolean
// process([]) // Error重载是编译时行为
TypeScript 的重载仅在编译时起作用,运行时仍然是单个 JavaScript 函数。这与 Java/C++ 的编译期重载解析不同。实现签名的类型通常是所有重载的联合类型。
// 实现签名的类型必须足够宽泛以覆盖所有重载
function len(s: string): number
function len(arr: unknown[]): number
function len(x: string | unknown[]): number {
return x.length
}差异分析
| 维度 | Go | TypeScript |
|---|---|---|
| 函数类型 | func(a int) int | (a: number) => number |
| 重载 | 不支持 | 支持(编译时多个签名) |
| 可选参数 | 不支持 | param?: type |
| 默认参数 | 不支持 | param = value |
| 变参 | args ...int | ...args: number[] |
| 多返回值 | 原生支持 (int, error) | 需用元组或对象模拟 |
| this 类型 | 无此概念(基于值传递) | this: Type |
Go 的多返回值模式在 TS 中的等效写法
// Go: func div(a, b int) (int, error)
function div(a: number, b: number): [number, Error | null] {
if (b === 0) return [0, new Error('division by zero')]
return [a / b, null]
}
// 更 TS 风格:抛出异常或返回 Result 类型
function safeDiv(a: number, b: number): number {
if (b === 0) throw new Error('division by zero')
return a / b
}
// 或者使用 discriminated union
type Result<T> =
| { ok: true; value: T }
| { ok: false; error: Error }
function div2(a: number, b: number): Result<number> {
if (b === 0) return { ok: false, error: new Error('division by zero') }
return { ok: true, value: a / b }
}Bad Practice
// Bad: 过多的重载(难以维护)
function transform(s: string): string
function transform(n: number): number
function transform(b: boolean): boolean
function transform(d: Date): string
function transform(a: string[]): string[]
function transform(x: unknown): unknown {
// 实现过于复杂
return x
}
// Bad: 用重载模拟默认参数
function connect(): void
function connect(timeout: number): void
function connect(timeout?: number): void {
// 简单用默认参数即可
}
// Bad: 可选参数在必选之前
// function bad(opt?: string, req: string) {} // 语法错误
// Bad: 箭头函数中使用 this 而无类型标注
const obj = {
name: 'test',
bad: () => {
// 这里的 this 是全局对象,非 obj
// return this.name
},
}Best Practice
// Good: 用联合类型替代复杂重载
type Input = string | number | boolean
function process(input: Input): string {
return String(input).toUpperCase()
}
// 或者用条件类型
function process<T extends Input>(input: T): T extends string ? string : number {
// 实现
return input as any
}
// Good: 限制重载数量(不超过 3-4 个重载)
function createElement(tag: 'div'): HTMLDivElement
function createElement(tag: 'span'): HTMLSpanElement
function createElement(tag: 'a'): HTMLAnchorElement
function createElement(tag: string): HTMLElement {
return document.createElement(tag)
}
// Good: 函数重载 + 泛型结合
function first<T>(arr: T[]): T | undefined
function first<T>(arr: T[], defaultVal: T): T
function first<T>(arr: T[], defaultVal?: T): T | undefined {
return arr.length > 0 ? arr[0] : defaultVal
}
// Good: 使用函数类型表达式提高可组合性
type Validator<T> = (value: T) => boolean
type Transformer<I, O> = (input: I) => O
const isPositive: Validator<number> = (n) => n > 0
const toString: Transformer<number, string> = (n) => n.toString()
// Good: 对象方法避免箭头函数以正确绑定 this
class Counter {
count = 0
increment() {
this.count++ // this 正确绑定到实例
}
}箭头函数与 this
JavaScript 的 this 规则与 Go 完全不同。箭头函数捕获定义时的 this,而普通函数在调用时确定 this。
// 箭头函数捕获外层 this
class Logger {
prefix = '[LOG]'
logLater = () => {
// 这里的 this 始终指向 Logger 实例
console.log(this.prefix, 'message')
}
}
// 作为回调传递时箭头函数更安全
button.addEventListener('click', () => {
// 这里的 this 不指向 button,想访问 button 需用 event.target
})总结
| Go 概念 | TypeScript 对应 |
|---|---|
func add(a, b int) int | function add(a: number, b: number): number |
func add(a ...int) | function add(...a: number[]) |
| 不支持重载 | function f(x: T): R + 多个签名 |
| 不支持默认参数 | function f(x = 42) |
多返回值 (T, error) | [T, Error] 元组或 Result 类型 |
| 方法接收者 | this 类型标注 |
关键认知转变:TS 的函数系统更加灵活,但也带来了 this 的复杂性。下一章将介绍 类与面向对象编程。