函数

函数是 JavaScript 和 TypeScript 中最基本的代码组织单元。TypeScript 为函数添加了类型系统,让函数的参数和返回值都有明确的类型约束,提高了代码的安全性和可读性。

函数定义

TypeScript 支持多种函数定义方式,每种方式都可以添加类型注解。

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

const subtract = function(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

console.log(add(10, 5))
console.log(subtract(10, 5))
console.log(multiply(10, 5))
console.log(divide(10, 5))

四种写法各有特点:函数声明会提升,函数表达式更灵活,箭头函数适合简短逻辑。

参数类型

函数参数可以指定类型,调用时 TypeScript 会检查参数类型是否匹配。

function greet(name: string, age: number): string {
  return `你好,我是 ${name},今年 ${age} 岁`
}

console.log(greet("张三", 25))

如果传入错误类型的参数,编译时会报错。这避免了运行时的类型错误。

返回值类型

函数可以指定返回值类型,TypeScript 会检查返回值是否符合声明的类型。

function isAdult(age: number): boolean {
  return age >= 18
}

function getAge(birthYear: number): number {
  return new Date().getFullYear() - birthYear
}

console.log(isAdult(20))
console.log(getAge(1990))

返回值类型可以省略,TypeScript 会自动推断。但显式声明返回值类型可以让代码意图更清晰。

函数类型表达式

可以用类型表达式描述函数的类型,常用于定义回调函数的类型。

type MathOperation = (a: number, b: number) => number

const calculate = (op: MathOperation, a: number, b: number): number => {
  return op(a, b)
}

const add: MathOperation = (a, b) => a + b
const subtract: MathOperation = (a, b) => a - b

console.log(calculate(add, 10, 5))
console.log(calculate(subtract, 10, 5))

MathOperation 定义了一个函数类型,接受两个数字参数,返回一个数字。

调用签名

函数类型也可以用调用签名的形式定义,这种写法更接近接口的定义方式。

interface Calculator {
  (a: number, b: number): number
}

const add: Calculator = (a, b) => a + b

console.log(add(10, 5))

调用签名常用于描述可以调用的对象,特别是那些既有属性又能调用的函数。

void 返回类型

不返回值的函数使用 void 类型。

function log(message: string): void {
  console.log(`[LOG] ${message}`)
}

function forEach<T>(arr: T[], callback: (item: T) => void): void {
  for (const item of arr) {
    callback(item)
  }
}

log("应用启动")
forEach([1, 2, 3], (n) => console.log(n * 2))

void 表示函数没有返回值,或者返回值不重要。

never 返回类型

永不返回的函数使用 never 类型,比如抛出异常或无限循环的函数。

function throwError(message: string): never {
  throw new Error(message)
}

function infiniteLoop(): never {
  while (true) {}
}

function fail(message: string): never {
  return throwError(message)
}

never 表示函数永远不会正常结束,这在类型推断中很有用。

函数重载

函数重载允许一个函数有多种调用签名,根据参数类型执行不同的逻辑。

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))

重载签名描述了函数的不同调用方式,实现签名包含实际的逻辑代码。

实际应用

函数类型在实际开发中有很多应用场景。比如定义事件处理器的类型:

type EventHandler = (event: Event) => void

interface EventEmitter {
  on(event: string, handler: EventHandler): void
  emit(event: string, data?: any): void
}

class SimpleEmitter implements EventEmitter {
  private handlers: Map<string, EventHandler[]> = new Map()

  on(event: string, handler: EventHandler): void {
    const handlers = this.handlers.get(event) || []
    handlers.push(handler)
    this.handlers.set(event, handlers)
  }

  emit(event: string, data?: any): void {
    const handlers = this.handlers.get(event) || []
    handlers.forEach(handler => handler(data))
  }
}

const emitter = new SimpleEmitter()
emitter.on("message", (e) => console.log("收到消息:", e))
emitter.emit("message", "你好")

小结

TypeScript 的函数类型系统让函数的定义更加严谨。通过参数类型和返回值类型,可以在编译时发现类型错误。函数类型表达式和调用签名提供了灵活的类型定义方式。掌握函数类型是编写类型安全代码的基础。