【问题标题】:Make zod do top-level type checking on .parse() input让 zod 对 .parse() 输入进行顶级类型检查
【发布时间】:2021-12-11 15:46:23
【问题描述】:

我知道 zod 的目的是解析不受信任的输入数据并断言它的类型与您的架构匹配。

但通常这些数据是通过网络 API 进入的,保证至少其顶级形状,如 stringobject

zod 似乎有必要对 parse() 进行这种顶级类型检查,如果只是为了防止诸如拼写错误之类的愚蠢错误。但是,貌似没有。

作为一个简化的例子来说明

const schema = z.string().email();
schema.parse(1); // no type error here - why?

似乎parse(1) 应该有一个编译时类型错误,因为我们知道文字number 1 不可能在运行时正确验证。我们不能用一些随机的string 输入来做到这一点 - 需要运行时解析以确保它是有效的电子邮件 - 但这里的 number 似乎是明显的程序员错误,甚至不应该编译。 p>

一个更实际的例子,导致我提出这个问题

async function validateRequest(request: Request) {
  const someSchema = z.object({ ... })
  return someSchema.parse(request.json()) // didn't await request.json() so won't work
}

一个愚蠢的错误,比如省略上面的await,似乎应该很容易通过someSchema.parse()检查我传递给它的是object,而不是Promise<object>

那么,有没有办法使用 zod 启用这种顶级类型检查?

或者这种行为是出于某种我对 zod 的设计不了解的原因而故意的?

【问题讨论】:

    标签: typescript zod


    【解决方案1】:

    我试图在 Zod 中寻找一种方法来做到这一点,但找不到任何令人信服的东西。我怀疑可能不支持开箱即用的用例。如果您想继续使用 Zod 进行解析,一个建议是创建一个具有更严格类型的包装函数,该函数在内部调用您的 Zod 架构。

    // I went for a record type because Promise<any> is assignable to object
    function parseResponse(input: Record<string, unknown>) {
      const someSchema = z.object({ ... });
      return someSchema.parse(input)
    }
    async function validateRequest(request: Request) {
      return parseResponse(request.json()) // This will throw because now it sees the promise
    }
    
    

    编辑:

    事实上,您通常可以像这样编写这种帮助器:

    import { z } from "zod";
    const schema = z.object({
      field: z.string(),
    })
    
    function restrict<T, Output, Def extends z.ZodTypeDef, Input = Output>(schema: z.ZodType<Output, Def, Input>) {
      return (t: T) => schema.parse(t);
    }
    type InferTypes<Z> = Z extends z.ZodType<infer Output, infer Defs, infer Input> ? [Output, Defs, Input] : [never, never, never];
    type InferOutput<Z> = InferTypes<Z>[0];
    type InferDefs<Z> = InferTypes<Z>[1];
    type InferInput<Z> = InferTypes<Z>[2];
    
    const validate = restrict<
      Record<string, unknown>,
      InferOutput<typeof schema>,
      InferDefs<typeof schema>,
      InferInput<typeof schema>
    >(schema);
    
    validate({}); // This typechecks because it's possible
    async function foo(req: Request) {
      return validate(req.json()); // This has a type error.
    }
    

    唯一真正的缺点是你需要做的类型推断堆栈才能让泛型工作。

    替代库

    如果您对替代库持开放态度,这是io-ts 以相对一流的方式支持的东西。您可以明确声明模式的输入和输出类型,默认情况下它们只是以unknown 开头。权衡是开发人员的经验。 io-ts 使用需要大量时间来学习的函数式范例。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2018-04-19
      • 2021-07-18
      • 2023-02-22
      • 2015-09-04
      • 2011-07-17
      • 2018-05-19
      • 1970-01-01
      相关资源
      最近更新 更多