TypeScript通用groupBy函数类型定义问题求助
解决TypeScript groupBy函数的类型推断问题
你编写的groupByList函数想要根据是否传入转换函数返回不同的类型,但当前类型定义存在两个核心问题:
- 不传
transformer时,返回类型变成Record<number, unknown[]>,而非预期的Record<number, {a: number}[]> - 传入
transformer时,返回类型是Record<number, ({a: number} | {b: number})[]>,而非预期的Record<number, {b: number}[]>
问题根源在于当前泛型定义没有区分transformer是否存在的场景,固定使用A | C的联合类型作为数组元素类型,且泛型C没有默认值导致类型推断异常。
方案一:使用函数重载(推荐)
通过函数重载明确两种调用场景的参数和返回类型,让TypeScript精准匹配推断:
// 重载签名:无转换函数的情况 export function groupByList<A extends {}, B extends keyof any>( extractKey: (o: A) => B, data: A[] ): Record<B, A[]>; // 重载签名:有转换函数的情况 export function groupByList<A extends {}, C, B extends keyof any>( extractKey: (o: A) => B, data: A[], transformer: (o: A) => C ): Record<B, C[]>; // 函数实现 export function groupByList<A extends {}, C = A, B extends keyof any>( extractKey: (o: A) => B, data: A[], transformer?: (o: A) => C ) { return data.reduce( (acc, cur) => ({ ...acc, [extractKey(cur)]: (acc[extractKey(cur)] ?? []).concat( transformer ? transformer(cur) : cur ), }), {} as Record<B, C[]> ); }
方案二:使用条件类型
如果不想用重载,也可以通过条件类型根据transformer是否存在动态推断返回类型:
export const groupByList = <A extends {}, C = A, B extends keyof any>( extractKey: (o: A) => B, data: A[], transformer?: (o: A) => C ): transformer extends undefined ? Record<B, A[]> : Record<B, C[]> => { return data.reduce( (acc, cur) => ({ ...acc, [extractKey(cur)]: (acc[extractKey(cur)] ?? []).concat( transformer ? transformer(cur) : cur ), }), {} as transformer extends undefined ? Record<B, A[]> : Record<B, C[]> ); };
测试验证
两种方案都能让类型推断完全符合预期:
const myList = [{ a: 1 }, { a: 2 }, { a: 3 }]; const res1 = groupByList((x) => x.a, myList); // 类型:Record<number, { a: number; }[]> ✅ const res2 = groupByList((x) => x.a, myList, (x) => ({ b: x.a })); // 类型:Record<number, { b: number; }[]> ✅
关键调整点
- 给泛型
C设置默认值C = A,确保不传转换函数时,C自动匹配A的类型 - 通过重载或条件类型,区分有无转换函数的场景,避免返回不必要的联合类型
- 初始值的类型断言跟随返回类型动态调整,保证类型一致性
内容的提问来源于stack exchange,提问作者revilotom
相关产品推荐
相关产品推荐

