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

TypeScript嵌套属性可变元组返回问题:类型安全合并函数实现

如何实现类型安全的对象嵌套数组合并函数fc?

我想要编写一个名为fc的函数,接收两个对象并以完全类型安全的方式返回它们的合并版本:

const rfc = fc({ num: [1, 2, 3] }, { num: [4, 5, 6] });
//   ^? { num: [1, 2, 3, 4, 5, 6] }

平铺场景验证

先在平铺数组场景验证可变元组扩展的有效性:

type t1a = [1, 2, 3]
type t2a = [4, 5, 6]

type ra = [...t1a, ...t2a]
//   ^? [1, 2, 3, 4, 5, 6]
// 有效,符合预期

再用泛型函数实现平铺数组合并,类型推导正常:

function fa<T1 extends number[], T2 extends number[]>(t1: [...T1], t2: [...T2]): [...T1, ...T2] {
  return [...t1, ...t2];
}
const rfa = fa([1, 2, 3], [4, 5, 6]);
//   ^? [1, 2, 3, 4, 5, 6]
// 有效,符合预期

嵌套场景的问题

尝试直接对嵌套属性的类型进行扩展,结果符合预期:

type t1b = { num: [1, 2, 3] }
type t2b = { num: [4, 5, 6] }

type rtb = [...t1b['num'], ...t2b['num']]
//   ^? [1, 2, 3, 4, 5, 6]
// 有效,符合预期

但用泛型实现嵌套对象合并时,类型推导不符合预期:

function fc<T1 extends { num: number[] }, T2 extends { num: number[] }>(
  t1: T1,
  t2: T2,
): { num: [...T1['num'], ...T2['num']] } {
  return { num: [...t1.num, ...t2.num] as any };
}
const rfc = fc({ num: [1, 2, 3] }, { num: [4, 5, 6] });
//   ^?  {num: number[]}
// 为何不符合预期?不是应该是[1, 2, 3, 4, 5, 6]吗?

问题原因

问题出在泛型约束的T1 extends { num: number[] }和T2 extends { num: number[] }上——当你把T1['num']约束为number[]时,TypeScript会自动将传入的字面量元组[1,2,3]和[4,5,6]拓宽为number[]类型,而非保留它们的具体元组类型信息。这就导致后续的[...T1['num'], ...T2['num']]只能推导为number[],而非预期的合并元组。

正确实现方式

要保留元组的具体类型,需要让泛型参数直接捕获数组的元组类型,而非用number[]约束。修改后的函数如下:

function fc<TNum1 extends number[], TNum2 extends number[]>(
  t1: { num: [...TNum1] },
  t2: { num: [...TNum2] },
): { num: [...TNum1, ...TNum2] } {
  return { num: [...t1.num, ...t2.num] };
}

const rfc = fc({ num: [1, 2, 3] }, { num: [4, 5, 6] });
//   ^? { num: [1, 2, 3, 4, 5, 6] }

扩展:支持任意键的通用合并函数

如果想要支持任意键名的对象嵌套数组合并,可以用更通用的泛型实现:

type MergeNestedArrays<T1, T2> = {
  [K in keyof T1 & keyof T2]: 
    T1[K] extends any[] 
      ? T2[K] extends any[] 
        ? [...T1[K], ...T2[K]] 
        : T1[K]
      : T1[K]
};

function mergeNestedArrays<T1, T2>(t1: T1, t2: T2): MergeNestedArrays<T1, T2> {
  const result = {} as MergeNestedArrays<T1, T2>;
  const keys = new Set([...Object.keys(t1), ...Object.keys(t2)]);
  
  for (const key of keys as Array<keyof T1 & keyof T2>) {
    const val1 = t1[key];
    const val2 = t2[key];
    if (Array.isArray(val1) && Array.isArray(val2)) {
      result[key] = [...val1, ...val2] as any;
    } else {
      result[key] = val1 as any;
    }
  }
  
  return result;
}

// 测试
const merged = mergeNestedArrays(
  { num: [1,2,3], str: ['a','b'] },
  { num: [4,5,6], str: ['c','d'] }
);
//   ^? { num: [1, 2, 3, 4, 5, 6], str: ["a", "b", "c", "d"] }

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.02 20:17:40