You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

TypeScript:groupingBy函数动态子类型适配问题

解决groupingBy函数根据Seed键类型动态返回类型的问题

问题概述

你编写的groupingBy函数在seed传入包含特定字符串子类型键(如'big')的对象时,能正确返回「固定键必选数组 + 其他字符串键可选数组」的联合类型;但当seed的键类型为泛型string时,返回类型错误允许无检查的键访问,导致像grouped.small.forEach(...)这类不存在的键调用出现运行时错误。

解决方案

通过条件类型精准判断SeedKey的类型,动态生成对应的返回类型,修改后的函数实现如下:

// 定义条件类型,根据SeedKey的类型分支返回结果类型
type GroupingResult<SeedKey extends string, Value> = 
  SeedKey extends string 
    ? { [key: string]: Value[] | undefined } 
    : { [key in SeedKey]: Value[] } & { [key: string]: Value[] | undefined };

function groupingBy<Item, Value, SeedKey extends string>(
  list: Item[],
  classifier: (item: Item) => string,
  mapper: (item: Item) => Value,
  seed: { [key in SeedKey]: Value[] }
): GroupingResult<SeedKey, Value> {
  return list.reduce((accumulator, item) => {
    const key = classifier(item);
    // 断言key为accumulator的键类型,避免类型报错
    (accumulator[key as keyof typeof accumulator] = accumulator[key as keyof typeof accumulator] || []).push(mapper(item));
    return accumulator;
  }, seed as GroupingResult<SeedKey, Value>); // 断言seed为目标结果类型
}

测试验证

场景1:seed包含特定子类型键

const identity = <T>(x: T) => x;
const numbers = [1, 2, 3, 4, 10, 100, 44, 22];

const grouped = groupingBy(numbers, (num) => (num >= 10 ? 'big' : 'small'), identity, { big: [] });
// 返回类型:{ big: number[] } & { [key: string]: number[] | undefined }

grouped.big.forEach((num) => console.log(num > 10)); // 正常,无需可选链
grouped.small?.forEach((num) => console.log(num <= 10)); // 需要可选链,符合预期

场景2:seed的键类型为泛型string

const numbers2 = [10, 100, 44, 22];
const seed: { [key: string]: number[] } = {};

const grouped2 = groupingBy(numbers2, (num) => (num >= 10 ? 'big' : 'small'), identity, seed);
// 返回类型:{ [key: string]: number[] | undefined }

grouped2.big?.forEach((num) => console.log(num > 10)); // 需要可选链,避免无检查访问
// grouped2.small.forEach(...) 会触发类型错误,提示small可能为undefined,符合预期

原理说明

  • 条件类型GroupingResult会在编译时判断SeedKey是否完全等于string:
    • 若是,返回仅包含可选字符串键的类型,强制所有键访问都需做存在性检查;
    • 若否(即SeedKey是string的子类型,如'big'),返回原有联合类型,保证固定键的必选访问权限。
  • 相比函数重载,条件类型能直接在泛型层面做精准的分支判断,避免重载匹配时的类型覆盖问题,确保返回类型完全符合seed的键类型特征。

内容的提问来源于stack exchange,提问作者C Taylor

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.01 09:55:31