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

如何为TypeScript中的对象聚合函数添加类型定义

为对象聚合函数添加正确的TypeScript类型定义

函数(JavaScript实现)

该函数接收初始对象和任意数量的新对象,将所有对象中同键名的数组合并:

export function aggregateObjects(initialObject, ...newObjects) {
  const aggregate = (objectA, objectB) => {
    return Object.keys(objectB).reduce(
      (acc, key) => ({
        ...acc,
        [key]: [...(acc[key] ?? []), ...(objectB[key] ?? [])],
      }),
      objectA
    );
  };

  return newObjects.reduce(
    (aggregatedObjects, newObject) => aggregate(aggregatedObjects, newObject),
    initialObject
  );
}

TypeScript使用示例

函数支持传入任意数量的新对象,正确调用方式如下:

const initialObject = {
  a: [objectOfType1],
};

const newObject1 = {
  a: [objectOfType2],
  b: [objectOfType1],
};

const newObject2 = {
  c: [objectOfType1, objectOfType2],
};

const result = aggregateObjects(initialObject, newObject1, newObject2);

期望的TypeScript类型结果

基于上述示例,返回值类型应自动推导为:

type Result = {
  a: (ObjectType1 | ObjectType2)[];
  b: ObjectType1[];
  c: (ObjectType1 | ObjectType2)[];
};

尝试过的方案(存在问题)

以下方案无法正确推导返回值类型,且内部需要手动忽略类型检查:

export function aggregateObjects<T extends object, U extends any[]>(
  initialObjects: T,
  ...newObjects: U
) {
  const aggregate = <A extends object, B extends object>(
    objectA: A,
    objectB: B
  ): CombineObjs<A, B> => {
    return (Object.keys(objectB) as Array<keyof B>).reduce(
      (acc, key) => ({
        ...acc,
        [key]: [
          // @ts-ignore
          ...(acc[key as keyof CombineObjs<A, B>] ?? []),
          // @ts-ignore
          ...(objectB[key] ?? []),
        ],
      }),
      objectA as unknown as CombineObjs<A, B>
    );
  };

  return newObjects.reduce(
    (aggregatedObjects, newObject) => aggregate(aggregatedObjects, newObject),
    initialObjects
  );
}

正确解决方案

我们需要定义递归的类型辅助工具,用来合并多个对象的键值类型(将同键的数组合并为联合类型数组),同时为函数添加正确的泛型约束:

步骤1:定义单个键的数组类型合并规则

type MergeArrayValues<T, U> = 
  T extends Array<infer TItem> 
    ? U extends Array<infer UItem>
      ? (TItem | UItem)[]
      : T
    : U extends Array<infer UItem>
      ? U
      : never;

步骤2:定义两个对象的合并类型

type MergeObjects<T extends object, U extends object> = {
  [K in keyof T | keyof U]: 
    K extends keyof T 
      ? K extends keyof U
        ? MergeArrayValues<T[K], U[K]>
        : T[K]
      : K extends keyof U
        ? U[K]
        : never;
};

步骤3:定义任意数量对象的递归合并类型

type MergeAllObjects<T extends object, U extends object[]> = 
  U extends [infer First extends object, ...infer Rest extends object[]]
    ? MergeAllObjects<MergeObjects<T, First>, Rest>
    : T;

步骤4:为函数添加泛型类型约束

export function aggregateObjects<
  T extends Record<string, unknown[]>,
  U extends Record<string, unknown[]>[]
>(initialObject: T, ...newObjects: U): MergeAllObjects<T, U> {
  const aggregate = <A extends Record<string, unknown[]>, B extends Record<string, unknown[]>>(
    objectA: A,
    objectB: B
  ): MergeObjects<A, B> => {
    return (Object.keys(objectB) as Array<keyof B>).reduce((acc, key) => {
      const existing = acc[key] ?? [];
      const incoming = objectB[key] ?? [];
      return {
        ...acc,
        [key]: [...existing, ...incoming] as MergeArrayValues<A[keyof A], B[keyof B]>
      };
    }, objectA as unknown as MergeObjects<A, B>);
  };

  return newObjects.reduce(
    (aggregated, obj) => aggregate(aggregated, obj),
    initialObject as unknown as MergeAllObjects<T, U>
  );
}

验证示例

使用示例代码时,TypeScript会自动推导出正确的返回值类型:

type ObjectType1 = { id: number };
type ObjectType2 = { name: string };

const initialObject = {
  a: [{ id: 1 } as ObjectType1],
};

const newObject1 = {
  a: [{ name: "test" } as ObjectType2],
  b: [{ id: 2 } as ObjectType1],
};

const newObject2 = {
  c: [{ id: 3 } as ObjectType1, { name: "foo" } as ObjectType2],
};

const result = aggregateObjects(initialObject, newObject1, newObject2);
// result类型自动推导为:
// {
//   a: (ObjectType1 | ObjectType2)[];
//   b: ObjectType1[];
//   c: (ObjectType1 | ObjectType2)[];
// }

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.22 20:27:30