3Programming TypeScript

函数

函数类型

函数类型重载this类型回调

🎯 AI 时代的学习建议:不要只读理论。理解这一章的设计动机(为什么需要这个概念?)和边界条件(什么情况下会失效?)。当 AI 生成的代码涉及这些概念时,你能判断它是否用对了场景。AI 能帮你总结内容,但无法替代你亲手阅读、思考、建立直觉的过程。

第3章:函数(Functions)

一、导读

1.1 本章学习目标

第3章讲解 TypeScript 中函数的类型化方式。函数是 JavaScript 的核心,TypeScript 为函数提供了丰富的类型标注能力。

本章的核心学习目标包括:

  • 掌握函数类型的声明方式:参数类型、返回类型、可选参数、默认参数
  • 理解函数重载(Overloading):TypeScript 如何实现类型安全的重载
  • 掌握 `this` 类型的声明:如何在 TypeScript 中正确标注 this
  • 理解回调函数的类型化:如何处理异步回调和事件处理器
  • 学会使用函数类型构建灵活的 API

1.2 为什么本章如此重要

JavaScript 的函数极其灵活——它可以作为参数传递、作为返回值、绑定不同的 this。这种灵活性在大型项目中容易出错。TypeScript 的函数类型系统让你能够精确地描述函数的签名,在编译期捕获参数类型错误、this 绑定错误等常见问题。

1.3 核心问题引导

TypeScript 的函数重载与 Java/C# 的重载有什么本质区别?

如何正确标注回调函数的类型?void 返回类型意味着什么?

this 参数如何在 TypeScript 中显式声明?

函数类型 (a: string) => number 和 { (a: string): number } 有什么区别?


二、核心概念详解

2.1 函数类型基础

typescript
// 完整函数类型
type AddFn = (a: number, b: number) => number;

const add: AddFn = (a, b) => a + b;

// 可选参数和默认参数
function greet(name: string, greeting?: string): string {
  return `${greeting ?? "Hello"}, ${name}!`;
}

greet("Alice");            // "Hello, Alice!"
greet("Alice", "Hi");      // "Hi, Alice!"

// 剩余参数
function sum(...numbers: number[]): number {
  return numbers.reduce((acc, n) => acc + n, 0);
}

2.2 函数重载

TypeScript 的函数重载通过声明多个函数签名来实现:

typescript
function format(value: string): string;
function format(value: number): string;
function format(value: Date): string;
function format(value: string | number | Date): string {
  if (typeof value === "string") return value;
  if (typeof value === "number") return value.toFixed(2);
  return value.toISOString();
}

format("hello");    // string
format(3.14);       // string
format(new Date()); // string
// format(true);    // 错误!没有匹配的签名

2.3 this 类型

typescript
interface UIElement {
  addClickListener(onClick: (this: void, e: Event) => void): void;
}

class Handler {
  info: string;
  
  onClickGood = (e: Event) => {    // 箭头函数,this 绑定到实例
    this.info = e.type;
  };
  
  onClickBad(this: Handler, e: Event) {    // 显式声明 this 类型
    this.info = e.type;
  }
}

2.4 回调函数类型

typescript
// void 返回类型的回调
function fetchData(callback: (data: string) => void) {
  callback("result");
}

// void 作为返回类型意味着"返回值会被忽略"
fetchData((data) => data.length);    // OK! 返回值被忽略

// Promise 和 async 函数
async function fetchUser(id: string): Promise<User> {
  const response = await fetch(`/api/users/${id}`);
  return response.json();
}

// 函数类型属性
interface EventEmitter {
  on(event: "click", handler: (x: number, y: number) => void): void;
  on(event: "keydown", handler: (key: string) => void): void;
  on(event: string, handler: (...args: any[]) => void): void;
}

三、实战案例

3.1 案例一:类型安全的事件系统

typescript
type EventMap = {
  click: { x: number; y: number };
  keydown: { key: string; code: string };
  resize: { width: number; height: number };
};

class TypedEventEmitter<T extends Record<string, any>> {
  private handlers = new Map<string, Set<Function>>();

  on<K extends keyof T>(event: K, handler: (data: T[K]) => void) {
    if (!this.handlers.has(event as string)) {
      this.handlers.set(event as string, new Set());
    }
    this.handlers.get(event as string)!.add(handler);
  }

  emit<K extends keyof T>(event: K, data: T[K]) {
    const handlers = this.handlers.get(event as string);
    handlers?.forEach(h => h(data));
  }
}

const emitter = new TypedEventEmitter<EventMap>();
emitter.on("click", ({ x, y }) => console.log(`Click at ${x}, ${y}`));
emitter.on("keydown", ({ key }) => console.log(`Key: ${key}`));
emitter.emit("click", { x: 100, y: 200 });

3.2 案例二:类型安全的管道函数

typescript
type Fn<A, B> = (input: A) => B;

function pipe<A>(value: A): A;
function pipe<A, B>(value: A, fn1: Fn<A, B>): B;
function pipe<A, B, C>(value: A, fn1: Fn<A, B>, fn2: Fn<B, C>): C;
function pipe<A, B, C, D>(value: A, fn1: Fn<A, B>, fn2: Fn<B, C>, fn3: Fn<C, D>): D;
function pipe(value: any, ...fns: Fn<any, any>[]) {
  return fns.reduce((acc, fn) => fn(acc), value);
}

const result = pipe(
  "42",
  (s: string) => parseInt(s, 10),
  (n: number) => n * 2,
  (n: number) => `Result: ${n}`
);
console.log(result);    // "Result: 84"

四、本章小结

函数类型:参数类型、返回类型、可选参数、默认参数、剩余参数。

函数重载:通过多个函数签名实现类型安全的重载。

this 类型:显式声明 this 参数类型,箭头函数自动绑定 this。

回调函数:void 返回类型表示"返回值被忽略"。


五、思考与练习

TypeScript 的函数重载与 Java 的重载有什么本质区别?

箭头函数和普通函数在 this 绑定上有什么区别?TypeScript 如何帮助你避免 this 错误?

编写一个类型安全的 memoize 函数,保留原函数的类型签名。


3/7函数
0%