如何在TypeScript泛型中为两个键分别指定不同类型?
问题:TypeScript中如何定义「group-by-and-sum」函数的泛型类型?
我正在编写一个针对JavaScript对象的「group-by-and-sum」函数,代码如下:
function groupBySum< GroupByKey extends string, SumKey extends string, Record extends ??? >(groupBy: GroupByKey, sumOver: SumKey, records: Record[]): Map<R[GroupByKey], number>{ ... }
调用示例如下:
>> groupBySum('color', 'age', cats) Map('black' => 3, 'orange' => 2, 'white' => 4)
其中groupBy对应的属性可以是任意类型,但sumOver对应的属性必须是number类型。请问此处Record的类型应该如何定义?或者更通用地说,该如何定义这些泛型类型?
解决方案
你可以通过泛型约束明确Record的结构,要求它必须包含GroupByKey和SumKey两个属性,且SumKey对应的属性值类型为number,GroupByKey对应的属性值可以是任意可哈希类型(Map键支持这类类型)。同时需要修正返回类型中错误的R,改为Record:
function groupBySum< GroupByKey extends string, SumKey extends string, Record extends { [K in GroupByKey]: unknown } & { [K in SumKey]: number } >(groupBy: GroupByKey, sumOver: SumKey, records: Record[]): Map<Record[GroupByKey], number> { return records.reduce((map, item) => { const key = item[groupBy]; const currentSum = map.get(key) || 0; map.set(key, currentSum + item[sumOver]); return map; }, new Map<Record[GroupByKey], number>()); }
泛型说明
- GroupByKey extends string:限定分组用的属性名必须是字符串类型(符合对象键的常规场景)
- SumKey extends string:限定求和用的属性名必须是字符串类型
- Record extends { [K in GroupByKey]: unknown } & { [K in SumKey]: number }:
- 第一个交叉类型
{ [K in GroupByKey]: unknown }确保Record包含GroupByKey指定的属性,属性值类型不限 - 第二个交叉类型
{ [K in SumKey]: number }确保Record包含SumKey指定的属性,且属性值必须是number类型
- 第一个交叉类型
- 返回类型Map<Record[GroupByKey], number>:明确返回的Map的键类型与原对象中分组属性的类型一致,值为求和后的数字
类型安全验证
当传入不符合要求的参数时,TypeScript会自动报错:
- 如果
sumOver指定的属性不是number类型(比如传入对应字符串类型的'name'),TS会提示类型不匹配 - 如果传入的
records数组中存在对象缺少groupBy或sumOver指定的属性,TS也会给出错误提示
内容的提问来源于stack exchange,提问作者Sasgorilla
相关产品推荐
相关产品推荐

