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

TypeScript如何为分组函数指定泛型返回对象类型?

如何为TypeScript的GROUP BY函数指定准确的返回类型?

当然可以!咱们来一步步调整你的代码,让TypeScript既能准确推断返回对象的类型,还能让函数更健壮,避免不必要的错误。

首先先说说你现有代码的小问题:

  • 返回类型写的是{},这在TypeScript里代表空对象,完全没法体现分组后每个键对应的值是T[]类型的数组;
  • key参数是string类型,没法保证这个键确实存在于T类型的对象上,容易出现传错键名的运行时错误。

改进后的代码

我们可以通过泛型约束和TypeScript的内置工具类型来解决这些问题:

export class GroupUtil {
    /**
     * Take a typed array and returns an object of similarly typed arrays 
     * for which the key is the specified parameter.
     */
    public static KeyArrayOn<T, K extends keyof T>(arr: T[], key: K): Record<T[K], T[]> {
        // 用类型断言初始化结果对象,告诉TS它的最终类型
        const res: Record<T[K], T[]> = {} as Record<T[K], T[]>;
        
        for (const item of arr) {
            const groupKey = item[key];
            // 分组逻辑和你原来的一致
            if (res[groupKey]) {
                res[groupKey].push(item);
            } else {
                res[groupKey] = [item];
            }
        }
        
        return res;
    }
}

关键改进点解释

  1. 泛型约束K extends keyof T:
    这一步约束了传入的key必须是T类型的有效属性名。比如你传入Customer[]时,key只能是Customer的属性(比如firstName),如果不小心传了个不存在的键名,TypeScript会直接在编译阶段报错,提前避免问题。

  2. 返回类型Record<T[K], T[]>:
    Record是TypeScript的内置工具类型,专门用来定义“键为某类型、值为某类型”的对象。这里T[K]就是你分组键的类型(比如Customer['firstName']是string),值的类型则是T[](比如Customer[]),完美匹配你想要的结果类型——TypeScript会明确知道返回对象的每个成员都是传入泛型的数组。

  3. 类型断言初始化结果:
    因为一开始res是空对象,TypeScript没法自动推断它的最终类型,所以我们用as Record<T[K], T[]>告诉编译器它的类型,避免类型报错。

使用示例

咱们用你提到的Customer类型来举个例子:

// 定义Customer类型
interface Customer {
    firstName: string;
    lastName: string;
    id: number;
}

// 测试数组
const customers: Customer[] = [
    { firstName: 'John', lastName: 'Doe', id: 1 },
    { firstName: 'Jane', lastName: 'Smith', id: 2 },
    { firstName: 'John', lastName: 'Smith', id: 3 },
];

// 按firstName分组
const groupedByFirstName = GroupUtil.KeyArrayOn(customers, 'firstName');
// 此时groupedByFirstName的类型是Record<string, Customer[]>
// 访问groupedByFirstName['John']时,TypeScript会明确知道它是Customer[]类型

额外优化(可选)

如果你希望分组键始终是字符串类型(比如即使原属性是number类型的id,也转成字符串作为对象键),可以把代码改成这样:

public static KeyArrayOn<T, K extends keyof T>(arr: T[], key: K): Record<string, T[]> {
    const res: Record<string, T[]> = {};
    for (const item of arr) {
        // 把分组键转成字符串
        const groupKey = String(item[key]);
        if (res[groupKey]) {
            res[groupKey].push(item);
        } else {
            res[groupKey] = [item];
        }
    }
    return res;
}

这样不管原属性是什么类型,分组后的键都会是字符串,兼容性更强。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.27 07:30:11