json Zod:从默认值创建基本体对象

tkqqtvp1  于 2023-10-21  发布在  其他
关注(0)|答案(3)|浏览(128)

我很确定这是存在的,但我还没有能够找到任何关于它,尽管一些挖掘。假设我有一个这样的zod模式:

const Person = zod.object({
    name: z.string().default(''),
    age: z.number().nullable();
});

有没有一种方法可以创建这样的东西:

const InstancePerson = {
    name: '',
    age: null
}

zod模式的?

ztmd8pv5

ztmd8pv51#

我知道我参加聚会有点晚了,但也许这会在将来帮助到别人。
您可以通过以下方式扩展zod模式:

const Person = zod.object({
    name: z.string().default(''),
    age: z.number().nullable().default(null)
}).default({}); // .default({}) could be omitted in this case but should be set in nested objects

现在,您可以通过调用来检索所需的输出:

const InstancePerson = Person.parse({});
pbossiut

pbossiut2#

从库中似乎没有直接的方法来做这类事情,但是您可以深入研究它们的_私有字段并获得您正在寻找的功能。
这种方法存在一些风险,因为库维护者通常不保证这些私有属性的稳定性。如果您依赖此行为,则可能需要格外小心版本颠簸。
好吧,免责声明,这样的事情是可能的。将其扩展到更多类型留给读者作为练习:

import { z } from "zod";

const schema = z.object({
  name: z.string(),
  age: z.number().nullable()
});

const schemaDefaults = <Schema extends z.ZodFirstPartySchemaTypes>(
  schema: Schema
): z.TypeOf<Schema> => {
  switch (schema._def.typeName) {
    case z.ZodFirstPartyTypeKind.ZodDefault:
      return schema._def.defaultValue();
    case z.ZodFirstPartyTypeKind.ZodObject: {
      // The switch wasn't able to infer this but the cast should
      // be safe.
      return Object.fromEntries(
        Object.entries(
          (schema as z.SomeZodObject).shape
        ).map(([key, value]) => [key, schemaDefaults(value)])
      );
    }
    case z.ZodFirstPartyTypeKind.ZodString:
      return "";
    case z.ZodFirstPartyTypeKind.ZodNull:
      return null;
    case z.ZodFirstPartyTypeKind.ZodNullable:
      return null;
    // etc
    default:
      throw new Error(`Unsupported type ${schema._type}`);
  }
};

console.log(schemaDefaults(schema));

在这里,我没有指定任何默认值,但代码仍然输出您所期望的内容。如果指定“foo”作为name的默认值,代码将输出{ name: "foo", age: null }
一种更短的方法是简单地挖掘到模式的_def中的一层,寻找要调用的defaultValue函数,但我认为给定的方法更有原则,因为它可以扩展到支持每个核心zod模式类型。
最后一个警告,有些zod类型不像其他类型那样容易处理。像z.number这样的东西可以合理地默认为0,但是z.unionz.intersection会有有趣的递归情况。
可能值得为这种处理构建一个库,或者在repo中打开一个问题,使其成为所提供的API的一部分。

0vvn1miw

0vvn1miw3#

所以我已经访问过这个线程几次了,现在我想给予我的意见,
因为我的问题是

我想通过利用默认值安全地示例化一个新的zod对象,但也希望我输入所需的非默认值。

所以.parse功能只接受unknown,并不能帮助我理解 * 什么 * 我需要指定什么以及什么是默认值。
现在,如果你有同样的问题,你想要像MyZodObject.createInstance这样的东西,那么我可能有一个简单的解决方案
解决方案为z.input<typeof MyShape>
这是做什么,它返回你的typescript类型,预计将有所有必要的(必需的)关键字和标记的其余部分,这将是由.default作为可选填写。
假设你有

const Model = z.object({
    title: z.string(),
    active: z.boolean().default(false)
})

type ModelOutput = z.infer<typeof Model>
// ^ type ModelOutput = {
//    title: string;
//    active: boolean;
//}

type ModelInput = z.input<typeof Model>
// ^ type ModelInput = {
//    title: string;
//    active?: boolean | undefined; // << DEFAULT
// }

一个泛型的Type-Safe方法,用默认值示例化Zod Object

const makeInstantiator =
  <T extends z.ZodType<any>>(model: T) =>
  (input: z.input<T>): z.output<T> => {
    return model.parse(input);
  };

const instantiateModel = makeInstantiator(Model);
// const instantiateModel: (input: {
//   title: string;
//   active?: boolean | undefined;
// }) => {
//   title: string;
//   active: boolean;
// }

相关问题