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

如何定义带层级泛型及其他字段的TypeScript接口?

TypeScript 层级位置类型定义实现问题

需求说明

我希望通过泛型类型 LocationBase 定义一组层级嵌套的位置类型:

type Region    = LocationBase<1, "region", Subregion>;
type Subregion = LocationBase<2, "subregion", Area>;
type Area      = LocationBase<3, "area">;

使其等价于手动编写的以下接口结构:

interface Region {
  level: 1;
  levelName: "region";
  children?: Subregion[];
}
interface Subregion {
  level: 2;
  levelName: "subregion";
  children?: Area[];
}
interface Area {
  level: 3;
  levelName: "area";
  children?: Location<4>;
}
interface Location<T extends number>{
  level: T;
  levelName: "location";
  children?: Location<PlusOne<T>>;
}

请问这个需求能否实现?

尝试的代码及报错

我编写了以下代码,但触发了两个TypeScript错误:

TS2716: Type parameter 'Child' has a circular default.
TS2344: Type 'PlusOne' does not satisfy the constraint 'number'

代码如下:

type ArrayWithLength<Len extends number, Arr extends never[] = []> = Arr["length"] extends Len ? Arr : ArrayWithLength<Len, [never, ...Arr]>;
type PlusOne<T extends number> = [unknown, ...ArrayWithLength<T>]["length"];

interface LocationBase<
  Level extends number = number,
  LevelName extends string = "location",
  Child = LocationBase, // Error
> {
  level: Level;
  levelName: LevelName;
  children?: Child[];
};

type Region = LocationBase<1, "region", Subregion>;
type Subregion = LocationBase<2, "subregion", Area>;
type Area = LocationBase<3, "area", LocationBase<4>>;

// type Level5ArrayExpected = LocationBase<4>["children"]; // unknown[]

解决方案

这个需求是可以实现的,核心是修复泛型默认值的循环引用问题,同时确保PlusOne工具类型的返回值满足number约束。以下是正确的实现代码:

方案1:简化的PlusOne实现

// 实现数字自增的类型工具,覆盖常用层级范围,超出范围返回number
type PlusOne<T extends number> = 
  T extends 0 ? 1 :
  T extends 1 ? 2 :
  T extends 2 ? 3 :
  T extends 3 ? 4 :
  T extends 4 ? 5 :
  number;

// 重新定义LocationBase,Child默认值为下一级的LocationBase
interface LocationBase<
  Level extends number = number,
  LevelName extends string = "location",
  Child = LocationBase<PlusOne<Level>>
> {
  level: Level;
  levelName: LevelName;
  children?: Child[];
}

// 定义目标层级类型
type Region = LocationBase<1, "region", Subregion>;
type Subregion = LocationBase<2, "subregion", Area>;
type Area = LocationBase<3, "area">; // 不传入Child时,自动关联LocationBase<4>

// 验证递归层级
type Level4Location = LocationBase<4>;
type Level5Children = Level4Location["children"]; // 类型为 LocationBase<5>[] | undefined

方案2:通用数组式PlusOne实现

如果需要支持任意数字的自增,可以用数组长度推导的方式,同时确保类型约束:

// 生成指定长度的数组类型
type ArrayWithLength<Len extends number, Arr extends any[] = []> = 
  Arr["length"] extends Len ? Arr : ArrayWithLength<Len, [...Arr, any]>;

// 实现通用的数字+1工具类型,确保返回值为number
type PlusOne<T extends number> = 
  [...ArrayWithLength<T>, any]["length"] extends infer L 
  ? L extends number ? L : never 
  : never;

interface LocationBase<
  Level extends number = number,
  LevelName extends string = "location",
  Child = LocationBase<PlusOne<Level>>
> {
  level: Level;
  levelName: LevelName;
  children?: Child[];
}

// 定义目标层级类型
type Region = LocationBase<1, "region", Subregion>;
type Subregion = LocationBase<2, "subregion", Area>;
type Area = LocationBase<3, "area">;

关键修复点

  1. 解决循环默认值错误:将Child的默认值从LocationBase改为LocationBase<PlusOne<Level>>,这样默认情况下会自动关联下一级的位置类型,避免直接引用自身导致的循环。
  2. 修复类型约束错误:确保PlusOne<Level>的返回值被TypeScript识别为number类型,无论是通过条件判断覆盖常用范围,还是通过数组推导加类型断言的方式,都能满足Level extends number的约束。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.28 14:47:10