801 字
4 分钟
04 · 函数类型深入
NOTE函数是 JS 的一等公民,TS 给函数类型提供了远超参数注解的能力:重载、this 约束、灵活的参数形态。本篇一次讲透。
1. 函数类型表达式
把”函数长什么样”抽成类型:
type Greet = (name: string) => string;
const sayHi: Greet = (n) => `hi ${n}`;注意区分两种箭头:
(name: string) => string是类型里的函数签名。(name: string): string => {}是值里的箭头函数(返回类型注解在参数外)。
2. 参数形态
// 可选参数 ?function build(name: string, age?: number): string { return age ? `${name}/${age}` : name;}
// 默认参数(默认参数自动推断类型)function fetch(url: string, method = "GET"): void {}
// rest 参数function sum(...nums: number[]): number { return nums.reduce((a, b) => a + b, 0);}规则:可选参数和默认参数必须放在必填参数之后;rest 永远最后。
3. 函数重载 overload
JS 函数常根据参数类型/个数返回不同结果。TS 用重载签名 + 实现签名表达:
// 重载签名(对外可见,无函数体)function reverse(x: string): string;function reverse(x: number): number;
// 实现签名(对外不可见,需兼容所有重载)function reverse(x: string | number): string | number { if (typeof x === "string") return x.split("").reverse().join(""); return Number(String(x).split("").reverse().join(""));}
reverse("abc"); // ✅ stringreverse(123); // ✅ numberreverse(true); // ❌ 不在重载列表中要点:
- 重载签名可以有多个,实现签名只有一个且必须兼容全部重载。
- 调用时按重载签名匹配,类型更精确(返回值是
string而非string | number)。
4. this 类型与 this 参数
JS 里 this notoriously 难推断。TS 用显式 this 参数标注:
interface Counter { count: number; inc(this: Counter): void;}const c: Counter = { count: 0, inc() { this.count++; },};不写 this: Counter 时,方法内 this 会被推断为 Counter,一般够用。但当函数被抽离调用时容易丢 this:
function print(this: User): void { console.log(this.name);}const u: User = { name: "Tom", print } as any;// 把 print 当回调传出去时,若没约束 this 会报错this 参数必须放在参数列表第一位,且只用于类型检查,编译后会被擦除。
5. 回调里的 this
常见坑:回调函数中 this 指向丢失。配合 this 类型可约束:
interface Handlers { onClick(this: Button, e: Event): void;}class Button { label = "btn"; bind(h: Handlers) { document.addEventListener("click", () => h.call(this, event)); }}这样 h 里只能用 Button 的 this,避免回调里误用外层 this。
6. 构造函数类型
用 new 描述可 new 的东西:
type Constructor = new (name: string) => Animal;function create(Ctor: Constructor, name: string): Animal { return new Ctor(name);}小结
- 函数类型表达式
(a: T) => R用于注解函数变量。 - 可选
?、默认、rest 参数各有位置约束。 - 重载 = 多个对外签名 + 一个兼容实现,提升调用侧精度。
this参数放在第一位,约束方法/回调里的this,编译后擦除。
练习
- 写
type BinaryOp = (a: number, b: number) => number,实现add/sub/mul三个符合该类型的值。 - 用重载实现
pad(s: string, n: number): string与pad(n: number, len: number): string(数字补零)。 - 写一个需要
this: { value: number }的double(this: { value: number }): number方法并测试。 - 解释”实现签名必须兼容所有重载签名”的含义,并故意写个不兼容的看报错。