TypeScript通用统计计算器:如何基于mapper返回值推断calculator参数类型?
基于Mapper返回值自动推断Calculator参数类型的实现方案
核心思路是利用TypeScript的泛型特性,将每个统计项的mapper返回类型与calculator参数类型绑定,彻底消除手动类型断言的需求。
步骤1:定义泛型统计项类型
将原有的StatisticDefinition改为带泛型参数的类型,用泛型T表示mapper的返回值类型,让calculator的参数类型直接复用T:
type InputData = { foo: number[]; bar: number[]; baz: { qux: number; }; }; const input: InputData = { foo: [1, 2, 3], bar: [4, 5, 6], baz: { qux: 5, }, }; // 泛型版本的统计项定义,绑定mapper返回值与calculator参数类型 type StatisticDefinition<T> = { name: string; mapper: (input: InputData) => T; calculator: (mappedValue: T) => unknown; };
步骤2:用辅助函数触发自动推断
直接定义对象无法让TypeScript为每个属性单独推断泛型,因此需要一个辅助函数来完成自动推断:
// 辅助函数,用于自动识别每个统计项的泛型类型 function createStatisticDefinitions< T extends Record<string, StatisticDefinition<any>> >(defs: T) { return defs; }
步骤3:编写统计项(无需类型断言)
使用辅助函数创建统计项集合,此时每个calculator的参数类型会自动匹配对应mapper的返回类型:
const statisticDefinitions = createStatisticDefinitions({ averageFoo: { name: "Average Foo", mapper: (input) => input.foo, calculator: (foo) => { // foo自动推断为number[],无需as转换 return foo.reduce((a, b) => a + b, 0) / foo.length; }, }, barPlusQux: { name: "Bar Plus Qux", mapper: (input) => [input.bar, input.baz.qux] as const, calculator: ([bar, qux]) => { // bar自动推断为number[],qux自动推断为number return bar.reduce((a, b) => a + b, 0) + qux; }, }, });
提示:添加
as const可以让mapper返回的数组类型更精确(从模糊的(number[] | number)[]变为明确的readonly [number[], number]),进一步提升类型安全性。
步骤4:执行逻辑保持不变
原有循环计算统计值的代码无需修改,类型检查依然有效:
for (const [key, statisticDefinition] of Object.entries(statisticDefinitions)) { const mappedValue = statisticDefinition.mapper(input); const calculatedValue = statisticDefinition.calculator(mappedValue); console.log(`${key} (${statisticDefinition.name}): ${calculatedValue}`); }
扩展:明确统计结果类型
如果需要为每个统计项指定返回值类型,可以进一步扩展泛型:
type StatisticDefinition<TInput, TResult> = { name: string; mapper: (input: InputData) => TInput; calculator: (mappedValue: TInput) => TResult; }; function createStatisticDefinitions< T extends Record<string, StatisticDefinition<any, any>> >(defs: T) { return defs; } // 此时calculator的返回值类型也会被自动推断或手动指定 const statisticDefinitions = createStatisticDefinitions({ averageFoo: { name: "Average Foo", mapper: (input) => input.foo, calculator: (foo): number => { return foo.reduce((a, b) => a + b, 0) / foo.length; }, }, });
内容的提问来源于stack exchange,提问作者slhck
相关产品推荐
相关产品推荐

