无需重复定义键,如何生成带类型提示的对应Record类型Zod Schema?
问题:Zod Schema无法生成字段提示的解决方法
现有如下JSON对象:
const amenities = { "wifi": "Wifi", "tv": "Tv", "steamIron": "Steam iron", "kitchenette": "Kitchenette", "fridge": "Fridge", "airConditioner": "Air conditioner", "fan": "Fan", "gym": "Gym", "pool": "Pool", "parking": "Parking", "lounge": "Lounge", "laundry": "Laundry", "stove": "Stove", "balcony": "Balcony", "microwave": "Microwave", "oven": "Oven", "desk": "Desk", "towels": "Towels", "bedSheets": "Bed sheets", "studyRoom": "Study room", "chair": "Chair", "table": "Table", "elevator": "Elevator", "generator": "Generator", "security": "Security", "cafe": "Cafe", "restaurant": "Restaurant", "housekeeping": "Housekeeping" }
需求是创建一个Zod Schema,实现与interface Amenities extends Record<keyof typeof amenities, boolean> {}相同的效果:生成的对象必须包含amenities的所有键,且每个键的值为布尔类型。
曾尝试以下代码:
export const Amenities = z.record(z.boolean()).refine((val) => { const keys = Object.keys(amenities); for (const key of keys) { if (!(key in val)) { return false; } } return true; }, { message: 'Amenities should have all keys defined', }).nonstrict();
但通过该Schema推断类型后,创建对象时无法获得字段提示,请问该如何解决?
解决方案
你之前用z.record()的方式,类型推断会得到Record<string, boolean>,TypeScript无法识别具体的键名,所以不会有字段提示。正确的做法是基于amenities的键生成一个严格的对象Schema,具体实现如下:
import { z } from "zod"; const amenities = { /* 你的amenities对象 */ }; // 生成每个键对应boolean类型的Schema定义 const amenitiesSchemaEntries = Object.keys(amenities).map(key => [key, z.boolean()] as const ); // 创建严格的对象Schema export const AmenitiesSchema = z.object(Object.fromEntries(amenitiesSchemaEntries)); // 推断类型,和你需要的interface完全一致 export type Amenities = z.infer<typeof AmenitiesSchema>;
关键说明:
Object.keys(amenities).map(...):遍历amenities的所有键,为每个键创建[键名, z.boolean()]的条目,as const用来固定键名的字面量类型,确保TypeScript能识别具体的键。z.object(...):传入生成的条目对象,创建一个严格的对象Schema,要求必须包含所有指定键,且值为布尔类型。- 类型推断:通过
z.infer<typeof AmenitiesSchema>得到的类型,和你定义的interface Amenities extends Record<keyof typeof amenities, boolean> {}完全一致,创建对象时会自动提示所有字段。
如果需要允许额外的键(类似原代码的.nonstrict()),可以在对象Schema后调用.passthrough():
export const AmenitiesSchema = z.object( Object.fromEntries(amenitiesSchemaEntries) ).passthrough();
这样既满足了所有必填键的校验要求,又能获得完整的字段提示。
内容的提问来源于stack exchange,提问作者user21276090
相关产品推荐
相关产品推荐

