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

如何在TypeScript中传递泛型类型作为类型参数实现通用元组组合?

TypeScript泛型元组合并工具的泛化实现问题

问题背景

我编写了一个用于合并两个TypeScript元组的工具类型TupleIntersect,实现逻辑如下:

type TupleIntersect<L extends unknown[], R extends unknown[]> =
    L extends [infer LH, ...infer LT]
    ? R extends [infer RH, ...infer RT]
    ? [LH & RH, ...TupleIntersect<LT, RT>] // 左右都是非空元组
    : [LH & R[number], ...TupleIntersect<LT, R>] // 左非空,右任意
    : R extends [infer RH, ...infer RT]
    ? [RH & L[number], ...TupleIntersect<L, RT>] // 右非空,左任意
    : L extends []
    ? R extends []
    ? [] // 左右都是空元组
    : R[number] // 左空,右非空
    : R extends []
    ? L[number] // 右空,左非空
    : (L[number] & R[number])[]; // 左右都是数组类型

为了避免为元组联合等场景重复编写类似逻辑,我希望将其泛化,设计了TupleZip工具类型,通过传入不同的组合器实现不同的合并逻辑:

type TupleZip<L extends unknown[], R extends unknown[], Combinator, Default> =
    L extends [infer LH, ...infer LT]
    ? R extends [infer RH, ...infer RT]
    ? [Combinator<LH, RH>, ...TupleZip<LT, RT, Combinator, Default>]
    : [Combinator<LH, R[number]>, ...TupleZip<LT, R, Combinator, Default>]
    : R extends [infer RH, ...infer RT]
    ? [Combinator<L[number], R>, ...TupleZip<L, RT, Combinator, Default>]
    : L extends []
    ? R extends []
    ? []
    : [Combinator<Default, R[number]>]
    : R extends []
    ? [Combinator<L[number], Default>]
    : Combinator<L[number], R[number]>[];

预期的使用方式如下:

type Intersect<L, R> = L & R;
type TupleIntersect<L extends unknown[], R extends unknown[]> = TupleZip<L, R, Intersect, unknown>;

type Union<L, R> = L | R;
type TupleUnion<L extends unknown[], R extends unknown[]> = TupleZip<L, R, Union, never>;

但TypeScript编译器报错**“类型参数不是泛型类型”**,请问是否有合法实现方式?是否过度类比了C++的模板模板参数?


解决方案

TypeScript目前不支持C++风格的模板模板参数(即直接传递泛型类型作为另一个泛型的参数),但可以通过泛型函数类型模拟类型构造器的方式实现需求:

1. 定义组合器类型

将组合器设计为泛型函数类型,用于描述两个类型的组合逻辑:

// 交集组合器:返回两个类型的交集
type IntersectCombiner = <T, U>(t: T, u: U) => T & U;
// 并集组合器:返回两个类型的并集
type UnionCombiner = <T, U>(t: T, u: U) => T | U;

2. 实现通用的TupleZip

通过条件类型推断组合器的返回类型,完成元组的遍历与组合:

// 提取组合器的返回类型
type Combine<T, U, Combiner> = 
  Combiner extends <A, B>(a: A, b: B) => infer Res 
    ? Res 
    : never;

// 通用元组组合工具类型
type TupleZip<
  L extends unknown[],
  R extends unknown[],
  Combiner,
  Default
> = 
  L extends [infer LH, ...infer LT]
    ? R extends [infer RH, ...infer RT]
      ? [Combine<LH, RH, Combiner>, ...TupleZip<LT, RT, Combiner, Default>]
      : [Combine<LH, R[number], Combiner>, ...TupleZip<LT, R, Combiner, Default>]
    : R extends [infer RH, ...infer RT]
      ? [Combine<L[number], RH, Combiner>, ...TupleZip<L, RT, Combiner, Default>]
      : L extends []
        ? R extends []
          ? []
          : [Combine<Default, R[number], Combiner>]
        : R extends []
          ? [Combine<L[number], Default, Combiner>]
          : Combine<L[number], R[number], Combiner>[];

3. 使用示例

通过传入不同的组合器,快速实现元组交集、并集等逻辑:

// 元组交集
type TupleIntersect<L extends unknown[], R extends unknown[]> = TupleZip<L, R, IntersectCombiner, unknown>;
// 元组并集
type TupleUnion<L extends unknown[], R extends unknown[]> = TupleZip<L, R, UnionCombiner, never>;

原理说明

TypeScript不允许直接传递泛型类型别名作为参数,但可以通过泛型函数类型模拟“类型构造器”,再通过ReturnType和条件类型推断提取组合后的类型,以此绕过模板模板参数的限制。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.30 23:45:41