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

如何在TypeScript中定义泛型参数可变的递归类型列表?

TypeScript 可变泛型类型定义问题

问题背景

给定如下类型定义:

type AThingWithGenerics<A, B, C> = {
  name: string;
  someFunction: (id: number) => A;
  anotherFunction: () => B;
  loveSomeGenerics: (s: string) => { foo: C };
};

其中A、B、C是随不同实例变化的对象类型,示例如下:

const aThing: AThingWithGenerics<{ foo: string }, { foo: number, bar: string }, { bar: string }> = {
  name: "a_thing",
  someFunction: (id: number) => { return { foo: "bar" }; },
  anotherFunction: () => { return { foo: 3, bar: "foo" } },
  loveSomeGenerics: (s: string) => { return { foo: { bar: "In this case C is an object containing a string" + s } }; },
}

const anotherThing: AThingWithGenerics<{ bar: number }, { aString: string }, { bar: "test" }> = {
  name: "another_thing",
  someFunction: (id: number) => { return { bar: id + 1 }; },
  anotherFunction: () => { return { aString: "test" } },
  loveSomeGenerics: (s: string) => { return { foo: { bar: "test" } } },
}

可以创建由特定AThingWithGenerics实例组成的元组:

type ASpecificPileOfThings = [typeof aThing, typeof anotherThing];

也可以在A、B、C固定的前提下创建同类型列表:

type APileOfTheSameThing<A, B, C> = [AThingWithGenerics<A, B, C>];

需求与遇到的问题

需要为AThingWithGenerics新增related字段,存储多个相关的AThingWithGenerics实例,比如aThing的related可以包含[anotherThing],这就要求related数组中元素的A、B、C泛型参数是可变的。

原有实现如下,无法通过类型校验,因为不同实例的A、B、C类型无法统一:

type AThingWithGenericsII<A, B, C> = {
  name: string;
  someFunction: (id: number) => A;
  anotherFunction: () => B;
  loveSomeGenerics: (s: string) => { foo: C };
  related: Array<AThingWithGenericsII<A,B,C>>
}

const anotherThingWithRelated: AThingWithGenericsII<{ bar: number }, { aString: string }, { bar: "test" }> = {
  name: "another_thing",
  someFunction: (id: number) => { return { bar: id + 1 } },
  anotherFunction: () => { return { aString: "test" } },
  loveSomeGenerics: (s: string) => { return { foo: { bar: "test" } } },
  related: []
}

const aThingWithRelated: AThingWithGenericsII<{foo: string, bar: string }, { property: string }, { nestedFoo: number }> = {
  name: "a_thing_with_related",
  someFunction: (id: number) => { return { foo: "This is a string", bar: "" } },
  anotherFunction: () => { return { property: "bar" } },
  loveSomeGenerics: (s: string) => { return { foo: { nestedFoo: 3.1 } } },
  related: [anotherThingWithRelated]
}

解决方案

原有代码报错的核心原因是related字段的类型强制要求所有关联实例的A/B/C泛型和当前实例完全相同,不符合存储不同泛型实例的需求。

你可以先定义一个无泛型约束的顶层类型,所有AThingWithGenerics实例都符合这个类型的结构,再把related字段的类型设置为这个顶层类型的数组即可:

// 无泛型的顶层类型,作为所有泛型变体的公共父类型
type AnyAThing = {
  name: string;
  someFunction: (id: number) => unknown;
  anotherFunction: () => unknown;
  loveSomeGenerics: (s: string) => { foo: unknown };
  related: AnyAThing[];
};

// 泛型类型继承顶层类型,保留本身的泛型约束
type AThingWithGenerics<A, B, C> = AnyAThing & {
  someFunction: (id: number) => A;
  anotherFunction: () => B;
  loveSomeGenerics: (s: string) => { foo: C };
};

修改后你的示例代码就可以正常通过类型校验,同时每个实例本身的三个泛型约束依然生效,调用实例自身的方法时能拿到正确的返回类型。访问related数组内的元素时默认会拿到AnyAThing类型,如果需要用到具体类型可以手动做类型断言。

如果需要更精准的类型推导,你可以给泛型加第四个可选参数,显式指定related数组的元素类型:

type AThingWithGenerics<A, B, C, R extends AnyAThing = AnyAThing> = AnyAThing & {
  someFunction: (id: number) => A;
  anotherFunction: () => B;
  loveSomeGenerics: (s: string) => { foo: C };
  related: R[];
};

定义实例时如果明确知道related里的元素类型,传入对应的第四个泛型参数即可获得更精确的类型提示。


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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.09.29 22:54:04