TypeScript 如何做链式调用类型
你有没有遇到过这样的场景:用JavaScript写工具函数时,想把多个小功能串起来,比如“先过滤数组里的偶数,再给每个数乘2,最后转成字符串”,代码写成filterEven(arr).multiplyBy2().toString(),用着挺顺手的——但如果有人不小心传了个字符串进去,或者中间哪一步返回了错误类型,浏览器只会在运行时报错,找问题得翻半天代码。
现在有了TypeScript,这种“运行时才发现的错误”可以提前在写代码的时候就拦住,链式调用的类型也能轻松搞定。

什么是链式调用?先举个生活化的例子
咱们先不用讲术语,就说你早上做早餐的步骤:先拿吐司(第一步),再涂花生酱(第二步),最后加香蕉片(第三步)。这三个步骤是连着做的,做完一步才能做下一步,而且每一步的“原料”都是前一步的“成品”——拿了吐司才能涂酱,涂了酱才能加香蕉。
对应到代码里,就是每一个功能函数做完之后,返回的是自身这个对象,这样才能接着调用下一个函数,比如toast().spreadPeanutButter().addBanana()。如果某一步返回了别的东西,比如涂酱的时候不小心返回了个水果,下一步加香蕉就没法做了——TypeScript的链式调用类型,就是帮你把每一步的“原料”和“成品”都定好规则,错了就提前标红。
最基础的链式调用类型:用类和泛型搞定
咱们写个数组处理的小工具,先从最简单的来。比如要做一个能过滤、能转成数字的链式工具,核心是让每个方法都返回这个工具类的实例,这样才能接着调用下一个方法。
举个实际的代码例子,你一看就懂:
`typescript
class ArrayChain<T> {
constructor(private data: T[]) {}
// 过滤数组,只保留符合条件的元素,返回新的工具实例
filter(predicate: (item: T) => boolean) {
const filtered = this.data.filter(predicate);
// 返回新的ArrayChain,保持链式
return new ArrayChain(filtered);
}
// 把数组元素转成数字,只对字符串类型生效,返回数字类型的工具实例
toNumber() {
const converted = this.data.map(Number);
// 这里用泛型把返回的类型改成number,保证后续调用的类型正确
return new ArrayChain<number>(converted);
}
// 最后取结果,结束链式
getResult() {
return this.data;
}
}
`
你看,每一个方法(比如filter、toNumber)都返回ArrayChain的新实例,这样就能一直点下去。比如你写new ArrayChain(['1', '2', '3']).toNumber().filter(n => n > 1).getResult(),TypeScript会自动知道,toNumber之后的数组元素是数字,filter的参数也会自动提示你是数字,不会让你写n => n > '1'这种明显的错误。
进阶一点:给链式调用加“状态校验”
如果想让链式调用更严谨,比如“只能在转成数字之后调用filter”,可以用泛型来做状态区分。比如刚才的工具,加个类型参数Mode,表示当前是字符串还是数字状态:
`typescript
class TypedArrayChain<Data, Mode = 'string'> {
constructor(private data: Data[]) {}
// 转成数字,只有当前是string模式时才能用
toNumber(): TypedArrayChain<number, 'number'> {
const converted = (this.data as string[]).map(Number);
return new TypedArrayChain(converted);
}
// 过滤,不管是数字还是字符串模式都能用
filter(predicate: (item: Data) => boolean): TypedArrayChain<Data, Mode> {
const filtered = this.data.filter(predicate);
return new TypedArrayChain(filtered);
}
}
`
这时候如果你先filter再toNumber,TypeScript就会报错:因为刚创建的实例是string模式,filter完还是string模式,这时候直接调用toNumber就不符合规则,必须先filter再toNumber,完全贴合我们的需求。
日常用的小技巧:避免链式调用的常见错误
很多人写链式调用的类型时,容易犯一个错:返回this而不是新的实例。如果直接返回this,就会出现“改了一个对象,链式的所有实例都被改了”的bug,TypeScript也没法正确判断后续的类型——比如你filter之后返回this,那后续的toNumber还会被当成原来的类型,很容易出错。
另外,别把类型写得太死。比如如果你的链式调用支持不同类型的转换,就用泛型来动态指定,而不是硬写死number或string,这样工具的复用性会高很多。
最后总结一下:TypeScript做链式调用类型的核心,就是每一步方法返回当前类的实例,再用泛型和类型参数记录每一步的状态,这样既能保证代码用起来顺手,又能提前拦住大部分类型错误,不用等运行的时候才踩坑。