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

使用Zod带区分联合的循环模式时,如何为Getter添加正确的类型注解以实现准确类型推断?

Zod带区分联合的循环模式时,如何为Getter添加正确的类型注解以实现准确类型推断?

嘿,我完全懂你的困扰——想用Zod v4的ES getter语法来写这个循环的区分联合结构,还得从Schema自动推断出正确的类型,不用反过来先写类型再套Schema,结果现在items被推断成unknown[],试了注解也没效果,确实挺让人头疼的。

首先得点明你代码里的两个核心问题:

  • 循环引用的Schema处理:你的Collection Schema里直接引用了还没完全定义好的Collection本身,不管是运行时还是类型层面,Zod都没法直接处理这种“先引用后定义”的循环,这是导致类型推断成unknown的关键原因。
  • Getter的用途误解:你现在在Getter里返回的是一个Zod Schema(z.array(...)),但Zod的z.object里的Getter是用来定义数据对象的Getter属性,而不是返回Schema本身。这就导致Zod完全搞不清你这个items属性的真实数据形状,自然推断成unknown了。

接下来给你正儿八经的解决方案,完全符合你的需求:从Schema生成类型、用Zod v4的Getter语法、处理循环的区分联合:

正确的代码实现

import { z } from "zod";

// 1. 先定义Story的Schema,没有循环引用,直接写就行
const Story = z.object({
  type: z.literal("story"),
  story: z.looseObject({}),
});

// 2. 提前声明Collection类型的占位符,用来处理循环引用的类型推断
type Collection = z.infer<typeof CollectionSchema>;

// 3. 定义Collection的Schema,用z.lazy处理循环引用,同时用Getter定义items属性
const CollectionSchema = z.object({
  type: z.literal("collection"),
  // 给Getter指定返回类型:数组元素是Story或Collection的推断类型
  get items(): Array<z.infer<typeof Story> | Collection> {
    // 这里返回的是符合Schema的实际数组数据示例,你可以根据需求调整
    return [];
  },
  // 如果你真实需求是items属性的Schema是循环的区分联合数组(而非Getter返回数据),写法如下:
  // items: z.array(z.discriminatedUnion("type", [Story, z.lazy(() => CollectionSchema)]))
});

// 4. 从Schema推断出最终的类型,此时items的类型是Array<Story | Collection>,完全正确
type StoryType = z.infer<typeof Story>;
type CollectionType = z.infer<typeof CollectionSchema>;

关键细节解释

  • 用z.lazy处理循环引用:如果你的需求是items属性是一个数组类型(而非Getter返回数据),必须用z.lazy(() => CollectionSchema)来延迟加载对Collection的引用,这样Zod在运行时和类型层面都能正确解析这个循环的区分联合。
  • Getter的类型注解:当你用Getter定义数据对象的属性时,直接给Getter指定返回类型为Array<z.infer<typeof Story> | Collection>,这个类型完全从已有的Schema推断而来,符合你“从Schema生成类型”的要求。
  • 避免Schema和数据混淆:一定要区分“定义数据形状的Schema”和“符合Schema的实际数据”——Zod的核心是定义数据的形状,Getter语法在z.object里是用来描述数据对象的Getter属性,而不是用来返回Schema的。

如果你确实需要在Getter里返回Zod Schema(比如动态生成Schema的场景),那得这么调整类型注解,解决循环推断的问题:

import { z } from "zod";

const Story = z.object({
  type: z.literal("story"),
  story: z.looseObject({}),
});

// 提前声明Schema的类型占位符
type CollectionSchemaType = z.ZodObject<{
  type: z.ZodLiteral<"collection">;
  items: {
    get: () => z.ZodArray<z.ZodDiscriminatedUnion<"type", [typeof Story, CollectionSchemaType]>>;
  };
}>;

const CollectionSchema: CollectionSchemaType = z.object({
  type: z.literal("collection"),
  get items(): z.ZodArray<z.ZodDiscriminatedUnion<"type", [typeof Story, CollectionSchemaType]>> {
    return z.array(
      z.discriminatedUnion("type", [Story, CollectionSchema])
    ) as z.ZodArray<z.ZodDiscriminatedUnion<"type", [typeof Story, CollectionSchemaType]>>;
  },
});

// 此时推断的Collection类型里,items是一个返回Zod数组Schema的Getter函数
type CollectionType = z.infer<typeof CollectionSchema>;

这样调整后,不管是哪种场景,类型推断都会完全符合你的预期,再也不会出现unknown[]的问题啦。

内容来源于stack exchange

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.04.08 09:18:03