函数是 JavaScript 的核心。TypeScript 为函数提供了完整的类型支持,包括参数类型、返回值类型、可选参数、函数重载等特性。
函数类型基础#
参数和返回值类型#
// TypeScript 5.x
// 函数声明function add(a: number, b: number): number { return a + b}
// 箭头函数const multiply = (a: number, b: number): number => { return a * b}
// 简写(单表达式)const divide = (a: number, b: number): number => a / b
// 无返回值使用 voidfunction log(message: string): void { console.log(message)}返回值类型推断#
TypeScript 可以自动推断返回值类型:
// 自动推断返回值为 numberfunction add(a: number, b: number) { return a + b}
// 自动推断返回值为 stringfunction greet(name: string) { return `Hello, ${name}!`}
// 🔶 建议:公共 API 显式标注返回值,内部函数可以依赖推断export function calculateTotal(prices: number[]): number { return prices.reduce((sum, price) => sum + price, 0)}函数类型表达式#
可以单独定义函数的类型:
// 函数类型表达式type AddFunction = (a: number, b: number) => number
const add: AddFunction = (a, b) => a + b
// 作为参数类型function calculate( fn: (x: number, y: number) => number, a: number, b: number): number { return fn(a, b)}
calculate(add, 10, 5) // 15calculate((x, y) => x - y, 10, 5) // 5完整的函数类型#
// 使用 type 定义type GreetFunction = (name: string, greeting?: string) => string
// 使用 interface 定义(调用签名)interface GreetInterface { (name: string, greeting?: string): string}
const greet: GreetFunction = (name, greeting = 'Hello') => { return `${greeting}, ${name}!`}可选参数#
使用 ? 标记可选参数:
function greet(name: string, title?: string): string { if (title) { return `${title} ${name}` } return name}
greet('张三') // "张三"greet('张三', '博士') // "博士 张三"🔶 可选参数必须放在必需参数之后:
// ❌ 错误:可选参数不能在必需参数之前// function wrong(a?: number, b: number) {}
// ✅ 正确function correct(a: number, b?: number) {}可选参数的类型是 T | undefined:
function example(value?: string) { // value 的类型是 string | undefined console.log(value?.toUpperCase()) // 需要安全访问}默认参数#
默认参数会自动推断类型,且不需要 ?:
function greet(name: string, greeting: string = 'Hello'): string { return `${greeting}, ${name}!`}
greet('张三') // "Hello, 张三!"greet('张三', '你好') // "你好, 张三!"
// 默认参数可以放在前面(但调用时需要传 undefined)function example(first: number = 10, second: number): number { return first + second}
example(undefined, 5) // 15默认参数 vs 可选参数#
// 可选参数:值可能是 undefinedfunction fn1(x?: number) { console.log(x) // number | undefined}
// 默认参数:始终有值function fn2(x: number = 10) { console.log(x) // number}
fn1() // undefinedfn2() // 10剩余参数#
使用 ... 收集剩余参数:
// 剩余参数是数组类型function sum(...numbers: number[]): number { return numbers.reduce((total, n) => total + n, 0)}
sum(1, 2, 3) // 6sum(1, 2, 3, 4, 5) // 15
// 结合普通参数function log(prefix: string, ...messages: string[]): void { messages.forEach((msg) => console.log(`${prefix}: ${msg}`))}
log('INFO', '开始处理', '处理完成')剩余参数与元组#
// 使用元组类型限制剩余参数function format(...args: [string, number, boolean]): string { const [name, age, active] = args return `${name}, ${age}岁, ${active ? '活跃' : '不活跃'}`}
format('张三', 25, true) // "张三, 25岁, 活跃"
// 带可变部分的元组function mixed(first: string, ...rest: [number, ...boolean[]]): void { console.log(first, rest)}
mixed('hello', 1, true, false, true)函数重载#
当函数根据不同参数有不同返回类型时,使用函数重载:
// 重载签名function parse(input: string): numberfunction parse(input: number): stringfunction parse(input: boolean): string
// 实现签名function parse(input: string | number | boolean): number | string { if (typeof input === 'string') { return parseInt(input, 10) } else if (typeof input === 'number') { return input.toString() } else { return input ? 'true' : 'false' }}
// 调用时有精确的类型const num = parse('123') // numberconst str = parse(456) // stringconst bool = parse(true) // string更复杂的重载示例#
// 根据参数数量有不同行为function createElement(tag: 'div'): HTMLDivElementfunction createElement(tag: 'span'): HTMLSpanElementfunction createElement(tag: 'input'): HTMLInputElementfunction createElement(tag: string): HTMLElement
function createElement(tag: string): HTMLElement { return document.createElement(tag)}
const div = createElement('div') // HTMLDivElementconst input = createElement('input') // HTMLInputElementconst custom = createElement('custom-element') // HTMLElement🔶 重载注意事项:
// 重载签名的顺序很重要:更具体的放前面function process(x: string): string // 更具体function process(x: unknown): unknown // 更宽泛
function process(x: unknown): unknown { if (typeof x === 'string') { return x.toUpperCase() } return x}
// TypeScript 按顺序匹配,第一个匹配的类型生效process('hello') // 返回类型是 stringprocess(123) // 返回类型是 unknownthis 类型#
在方法中可以显式声明 this 的类型:
interface User { name: string greet(this: User): string}
const user: User = { name: '张三', greet() { return `Hello, ${this.name}` },}
user.greet() // "Hello, 张三"
// 单独调用会报错(this 类型不匹配)// const fn = user.greet;// fn(); // ❌ 错误this 参数#
function onClick(this: HTMLButtonElement, event: Event) { console.log(this.textContent) // this 是 HTMLButtonElement}
const button = document.querySelector('button')!button.addEventListener('click', onClick)回调函数类型#
// 定义回调类型type Callback = (error: Error | null, result?: string) => void
function fetchData(url: string, callback: Callback): void { try { // 模拟异步操作 setTimeout(() => { callback(null, 'data') }, 1000) } catch (err) { callback(err as Error) }}
fetchData('/api/data', (err, result) => { if (err) { console.error(err) return } console.log(result)})常见回调模式#
// 事件处理器type EventHandler<T = Event> = (event: T) => void
// 比较函数type Comparator<T> = (a: T, b: T) => number
// 转换函数type Mapper<T, U> = (item: T, index: number) => U
// 谓词函数type Predicate<T> = (item: T) => boolean
// 使用示例const numbers = [3, 1, 4, 1, 5, 9]
const isEven: Predicate<number> = (n) => n % 2 === 0const double: Mapper<number, number> = (n) => n * 2const compare: Comparator<number> = (a, b) => a - b
numbers.filter(isEven) // [4]numbers.map(double) // [6, 2, 8, 2, 10, 18]numbers.sort(compare) // [1, 1, 3, 4, 5, 9]构造函数类型#
// 构造函数类型使用 newtype Constructor<T> = new (...args: any[]) => T
class User { constructor(public name: string) {}}
function createInstance<T>(ctor: Constructor<T>, ...args: any[]): T { return new ctor(...args)}
const user = createInstance(User, '张三')console.log(user.name) // "张三"抽象构造函数#
// 可以实例化的构造函数type Constructable<T> = new (...args: any[]) => T
// 抽象构造函数(不能直接实例化)type AbstractConstructable<T> = abstract new (...args: any[]) => T
abstract class Animal { abstract speak(): void}
class Dog extends Animal { speak() { console.log('Woof!') }}
// 接受抽象类作为参数function makeAnimal(ctor: AbstractConstructable<Animal>) { // 不能直接 new,但可以用于类型约束}泛型函数预览#
函数可以使用泛型来保持类型关系:
// 泛型函数function identity<T>(value: T): T { return value}
identity<string>('hello') // stringidentity<number>(42) // numberidentity('hello') // 自动推断为 string
// 泛型箭头函数const getFirst = <T>(arr: T[]): T | undefined => arr[0]
getFirst([1, 2, 3]) // number | undefinedgetFirst(['a', 'b']) // string | undefined常见问题#
🙋 函数类型的参数名重要吗?#
参数名只用于文档目的,不影响类型兼容性:
type Fn1 = (x: number) => numbertype Fn2 = (y: number) => number
// Fn1 和 Fn2 是相同的类型const fn: Fn1 = (num) => num * 2🙋 void 和 undefined 返回值有什么区别?#
// void:函数不返回有意义的值function logVoid(): void { console.log('log') // 可以不写 return}
// undefined:必须显式返回 undefinedfunction logUndefined(): undefined { console.log('log') return undefined // 必须有这行}
// void 允许返回任何值(用于回调)type VoidFunc = () => voidconst fn: VoidFunc = () => 123 // ✅ 允许🙋 什么时候用重载,什么时候用联合类型?#
// 如果返回类型与参数类型无关,用联合类型function process(x: string | number): string | number { return typeof x === 'string' ? x.toUpperCase() : x * 2}
// 如果返回类型取决于参数类型,用重载function process2(x: string): stringfunction process2(x: number): numberfunction process2(x: string | number): string | number { return typeof x === 'string' ? x.toUpperCase() : x * 2}
// 重载版本有更精确的类型const s = process2('hello') // string(不是 string | number)const n = process2(42) // number(不是 string | number)总结#
| 特性 | 语法 | 用途 |
|---|---|---|
| 参数类型 | (a: number) | 限定参数类型 |
| 返回值类型 | (): number | 限定返回值类型 |
| 可选参数 | (a?: number) | 参数可以不传 |
| 默认参数 | (a = 10) | 参数有默认值 |
| 剩余参数 | (...args: number[]) | 收集多余参数 |
| 函数类型 | type Fn = (x: number) => number | 定义函数签名 |
| 函数重载 | 多个签名 + 实现 | 多种调用方式 |
下一篇我们将深入学习类型推断与类型断言,掌握 TypeScript 的类型推导机制。