TypeScript

函数类型定义了函数的参数类型和返回值类型。在 TypeScript 中,函数类型是一等公民,可以作为变量的类型、参数的类型或返回值的类型。

基本语法

函数类型使用箭头语法 (参数列表) => 返回值类型 来定义。

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

const add: AddFunction = (a, b) => {
  return a + b
}

console.log(add(10, 20))

AddFunction 描述了一个接受两个数字参数并返回数字的函数。变量 add 被标注为这个类型后,TypeScript 会自动推断参数 ab 的类型。

函数类型别名

使用 type 关键字可以给函数类型起别名,让代码更加清晰。

type Callback = (data: string) => void
type Validator = (value: unknown) => boolean
type Comparer<T> = (a: T, b: T) => number

const logCallback: Callback = (data) => {
  console.log(data)
}

const isString: Validator = (value) => {
  return typeof value === "string"
}

const numberComparer: Comparer<number> = (a, b) => {
  return a - b
}

logCallback("测试消息")
console.log(isString("hello"))
console.log([3, 1, 2].sort(numberComparer))

类型别名让复杂的函数类型变得易于理解和复用。

接口定义函数类型

接口也可以定义函数类型,使用调用签名语法。

interface SearchFunction {
  (source: string, pattern: string): boolean
}

const contains: SearchFunction = (source, pattern) => {
  return source.includes(pattern)
}

console.log(contains("hello world", "world"))
console.log(contains("hello world", "foo"))

接口定义的函数类型可以扩展,适合需要添加属性的函数对象。

构造签名

构造签名描述可以用 new 调用的函数,即构造函数。

interface Constructor {
  new (name: string): { name: string }
}

function createInstance(ctor: Constructor, name: string) {
  return new ctor(name)
}

class Person {
  constructor(public name: string) {}
}

const person = createInstance(Person, "张三")
console.log(person.name)

构造签名常用于工厂函数和依赖注入场景。

函数类型推断

TypeScript 可以根据上下文推断函数参数的类型。

const numbers = [1, 2, 3, 4, 5]

const doubled = numbers.map((n) => n * 2)
const evens = numbers.filter((n) => n % 2 === 0)
const sum = numbers.reduce((acc, n) => acc + n, 0)

console.log(doubled)
console.log(evens)
console.log(sum)

回调函数的参数类型会根据数组的类型自动推断,不需要显式标注。

函数类型作为参数

函数类型可以作为其他函数的参数类型,实现高阶函数。

type Transform<T> = (value: T) => T

function processArray<T>(arr: T[], transform: Transform<T>): T[] {
  return arr.map(transform)
}

const numbers = [1, 2, 3, 4, 5]
const squared = processArray(numbers, (n) => n * n)

console.log(squared)

Transform 类型描述了一个转换函数,processArray 接受这个类型的函数作为参数。

函数类型作为返回值

函数可以返回另一个函数,返回的函数也有明确的类型。

type Multiplier = (x: number) => number

function createMultiplier(factor: number): Multiplier {
  return (x) => x * factor
}

const double = createMultiplier(2)
const triple = createMultiplier(3)

console.log(double(5))
console.log(triple(5))

createMultiplier 返回一个 Multiplier 类型的函数,这个函数会记住创建时的 factor 值。

函数类型重载

函数类型也可以定义重载,描述多种调用方式。

type StringOrNumber = {
  (value: string): string
  (value: number): number
}

const identity: StringOrNumber = (value: any) => value

console.log(identity("hello"))
console.log(identity(123))

重载类型描述了函数的不同调用签名,实现时需要处理所有情况。

可实例化的函数类型

有些函数既可以作为普通函数调用,也可以作为构造函数调用。

interface DateConstructor {
  new (value: number): Date
  (value: number): string
}

function useDate(ctor: DateConstructor) {
  const instance = new ctor(1234567890000)
  const result = ctor(1234567890000)
  return { instance, result }
}

这种类型同时包含调用签名和构造签名,描述了像 Date 这样的内置对象。

实际应用

函数类型在实际开发中常用于定义回调函数和事件处理器。

type RequestCallback = (error: Error | null, data?: any) => void

function fetchData(url: string, callback: RequestCallback): void {
  setTimeout(() => {
    if (url.includes("error")) {
      callback(new Error("请求失败"))
    } else {
      callback(null, { message: "成功" })
    }
  }, 1000)
}

fetchData("https://api.example.com/data", (error, data) => {
  if (error) {
    console.error(error.message)
  } else {
    console.log(data)
  }
})

RequestCallback 定义了标准的 Node.js 风格回调函数类型。

小结

函数类型是 TypeScript 类型系统的核心组成部分。通过函数类型,我们可以精确描述函数的输入和输出,实现类型安全的高阶函数。函数类型别名让复杂类型变得易于理解,接口定义的函数类型可以扩展和组合。掌握函数类型是编写可复用、类型安全代码的关键。