如何定义TypeScript连续映射函数数组的泛型MappingSequence类型
类型实现方案:连续映射函数序列
MappingSequence<A, Z> 需求回顾
需要定义一个泛型类型,约束元素为连续映射函数的数组,满足以下规则:
- 数组中第N个函数的入参类型,必须等于第N-1个函数的返回值类型
- 第一个函数的入参类型固定为
A - 最后一个函数的返回值类型固定为
Z - 支持任意长度的数组,包括长度为0的空数组(仅当
A extends Z时合法)
基础映射类型定义:
type Mapping<X, Y> = (item: X) => Y;
示例合法用法:
const mappings: MappingSequence<number, { duration: string }> = [ a => a * 60, // 入参number,返回number b => `${b} seconds`, // 入参number,返回string c => ({ duration: c }), // 入参string,返回{ duration: string } ];
对2依次执行上述函数,得到正确输出{ duration: '120 seconds' }。
最终实现代码
通过递归类型+累积泛型参数的方案实现,不需要手动声明中间类型,TypeScript会自动推导所有中间类型的约束:
type MappingSequence<A, Z, Acc extends ReadonlyArray<Mapping<any, any>> = []> = Acc extends [...unknown[], Mapping<unknown, infer LastOut>] ? LastOut extends Z // 当最后一个函数返回类型匹配Z时,当前累积的序列是合法类型,同时继续递归支持更长的序列 ? Acc | MappingSequence<A, Z, [...Acc, Mapping<LastOut, unknown>]> // 类型不匹配则继续向后递归追加函数 : MappingSequence<A, Z, [...Acc, Mapping<LastOut, unknown>]> // 初始分支:从入参类型A开始构建序列 : [] extends Acc ? (A extends Z ? [] : never) | MappingSequence<A, Z, [Mapping<A, unknown>]> : never;
配套工具函数(可选)
可以搭配类型安全的管道执行函数,直接运行映射序列:
function runMappings<A, Z>(initial: A, mappings: MappingSequence<A, Z>): Z { return mappings.reduce((current, fn) => fn(current), initial as never) as Z; } // 调用示例,返回值类型自动推导为{ duration: string } const result = runMappings(2, mappings); // 值为{ duration: '120 seconds' }
类型校验效果
- 合法序列会正常通过类型检查
- 相邻函数入参/返回值不匹配时,TS会直接抛出编译错误
- 空数组仅当A和Z类型一致时合法
- 支持任意长度的映射序列,不需要手动枚举长度分支
// 非法示例:第二个函数入参为string,但第一个函数返回number,类型报错 const badMappings: MappingSequence<number, { duration: string }> = [ a => a * 60, b => b.slice(), // 错误:number类型不存在slice方法 c => ({ duration: c }), ]; // 合法空数组:A和Z均为number const emptyMappings: MappingSequence<number, number> = [];
内容的提问来源于stack exchange,提问作者Ruben Verborgh
相关产品推荐
相关产品推荐

