我正在尝试创建一个函数,它接收一个 zod 对象并通过
keyof()
函数返回一个 zod 枚举。
我目前拥有的是:
const FormSchema = z.object({
username: z.string().trim().min(1).max(20),
password: z.string().trim().min(12).max(100),
rememberMe: z.coerce.boolean().optional().default(false),
redirectTo: z.string().trim().startsWith("/"),
});
type Schema<T extends z.AnyZodObject> = z.infer<T>
type SchemaEnum<T extends z.AnyZodObject> = ReturnType<T["keyof"]>;
function getEnumFromSchema<T extends z.AnyZodObject> (schema: T): SchemaEnum<T> {
const shape = schema._type;
return shape.keyof();
}
function test () {
const t = getEnumFromSchema(FormSchema);
}
当鼠标悬停在 codesandbox 上时,highliter 会显示以下内容
t
const t: z.ZodEnum<["username", "password", "rememberMe", "redirectTo"]>
这会返回一个枚举,但打字稿会抛出错误Type 'ZodEnum<never>' is not assignable to type 'ReturnType<T["keyof"]>'.
我一直试图用这个来获得一片绿叶,但对我来说似乎有些不对,我看在上帝的份上,找不到我要去哪里错了.
见上面的描述。
错误信息
类型“ZodEnum”不可分配给类型 '返回类型
'
表示
keyof()
的推断类型不是你所期望的。问题可能是 keyof()
返回对象所有可能键的联合,这不是 z.enum()
的有效类型。
一个解决方案是使用
z.union()
方法为对象的每个键组合单独的 z.literal()
验证器。您可以使用 Object.keys(schema)
创建一个密钥数组,然后使用 map()
创建一个 z.literal()
验证器数组。然后,您可以将验证器数组传递给 z.union()
以创建最终的枚举验证器。
这是更新后的代码:
const FormSchema = z.object({
username: z.string().trim().min(1).max(20),
password: z.string().trim().min(12).max(100),
rememberMe: z.coerce.boolean().optional().default(false),
redirectTo: z.string().trim().startsWith("/"),
});
type Schema<T extends z.AnyZodObject> = z.infer<T>
function getEnumFromSchema<T extends z.AnyZodObject>(schema: T) {
const keys = Object.keys(schema);
const validators = keys.map((key) => z.literal(key));
return z.union(validators);
}
function test() {
const t = getEnumFromSchema(FormSchema);
// t is inferred to be of type `z.ZodEnum<"username" | "password" | "rememberMe" | "redirectTo">`
}
请注意,您不再需要定义单独的
SchemaEnum
类型,因为 z.union(validators)
的推断类型是 z.ZodEnum<"username" | "password" | "rememberMe" | "redirectTo">
.