https://zod.dev
التحقق من صحة مخطط TypeScript-First مع الاستدلال الثابت
وقد تمت ترجمة هذه المستندات إلى الصينية.
npm
(عقدة/كعكة)deno.land/x
(Deno).shape
.keyof
.extend
.merge
.pick/.omit
.partial
.deepPartial
.required
.passthrough
.strict
.strip
.catchall
.element
.nonempty
.min/.max/.length
.parse
.parseAsync
.safeParse
.safeParseAsync
.refine
.superRefine
.transform
.default
.describe
.catch
.optional
.nullable
.nullish
.array
.promise
.or
.and
.brand
.readonly
.pipe
.pipe()
لإصلاح المشكلات الشائعة مع z.coerce
. ZOD عبارة عن إعلان مخطط من نوع TypeScript-First ومكتبة التحقق من الصحة. أنا أستخدم مصطلح "المخطط" للإشارة على نطاق واسع إلى أي نوع بيانات ، من string
بسيطة إلى كائن متداخل معقد.
تم تصميم Zod ليكون صديق المطورين قدر الإمكان. الهدف هو القضاء على إعلانات النوع المكررة. مع ZOD ، تعلن عن مصلحة مرة واحدة وسيستنتج ZOD تلقائيًا نوع النوع الثابت. من السهل إنشاء أنواع أبسط في هياكل بيانات معقدة.
Some other great aspects:
.optional()
) إرجاع مثيل جديديتم تقدير الرعاية في أي مستوى وتشجيع. إذا قمت ببناء منتج مدفوع باستخدام ZOD ، ففكر في أحد مستويات الشركات.
منصة إدارة المستخدمين الأكثر شمولاً
clerk.com
|
|
|
|
|
بروبينث | سيربوس | العددية | Trigger.Dev |
Transloadit | غير متلازمة | whop | cryptojobslist |
سهل. | نزل | StoryBlok | موكس |
براندون باير | Jiří Brabec | أليكس جوهانسون | Fungible Systems |
قابلة للتكيف | محفظة أفانا | جيسون لينجستورف | Global Illumination ، Inc. |
ماستربورن | ريان بالمر | مايكل سويني | NextBase |
عن بعد | كونور سينوت | محمد ألي آر | supatool |
هناك عدد متزايد من الأدوات التي تم تصميمها على قمة أو دعم zod أصلي! إذا قمت بإنشاء أداة أو مكتبة فوق Zod ، فأخبرني عنها على Twitter أو ابدأ مناقشة. سأضيفه أدناه وأغردها.
tRPC
: إنشاء واجهات برمجة تطبيقات الأنواع من طرف إلى طرف بدون GraphQL.@anatine/zod-nestjs
: أساليب المساعدة لاستخدام ZOD في مشروع NESTJS.zod-endpoints
: نقاط النهاية التي كتبها العقد الأول مع ZOD. Openapi متوافق.zhttp
: مكتبة HTTP متوافقة مع OpenAPI ومكتوبة بدقة مع إدخال ZOD والتحقق من صحة الاستجابة.domain-functions
: فصل منطق عملك من إطارك باستخدام وظائف قابلة للتكوين. مع الاستدلال من الدرجة الأولى من النهاية إلى النهاية مدعوم من مخططات zod.@zodios/core
: عميل API TypeScript مع وقت التشغيل والتحقق من وقت الترجمة المدعومة من AXIOS و ZOD.express-zod-api
: قم ببناء واجهات برمجة التطبيقات المستندة إلى Express مع التحقق من صحة مخطط الإدخال/الإخراج والآبار المخصصة.tapiduck
: Types type-typeafe JSON APIs مع Zod و Express ؛ قليلا مثل TRPC ، ولكن أبسط.koa-zod-router
: إنشاء طرق Typesafe في KOA مع التحقق من صحة I/O باستخدام ZOD.zod-sockets
: Socket.io microframework مع التحقق من صحة الإدخال/الإخراج ومواصفات غير متزامنة مدمجة react-hook-form
: محدد Zod Zod الطرف الأول لشكل خطاف رد الفعل.zod-validation-error
: Generate user-friendly error messages from ZodError
s.zod-formik-adapter
: محول Formik الذي تم صيانته في المجتمع لـ Zod.react-zorm
: مستقل <form>
توليد والتحقق من الصحة باستخدام ZOD.zodix
: Zod Utilities for FormData و urlsearchparams في Remix Loaders والإجراءات.conform
: مكتبة التحقق من صحة النماذج من نوع Typesafe للتعزيز التدريجي لأشكال HTML. يعمل مع Remix و Next.js.remix-params-helper
: تبسيط تكامل ZOD مع urlsearchparams و formdata لتطبيقات remix.formik-validator-zod
: مكتبة مصادقة متوافقة مع Formik والتي تبسط باستخدام Zod مع Formik.zod-i18n-map
: مفيد لترجمة رسائل خطأ ZOD.@modular-forms/solid
: مكتبة نموذجية النماذج لـ SolidJs التي تدعم ZOD للتحقق من الصحة.houseform
: مكتبة FAST Form التي تستخدم ZOD للتحقق من الصحة.sveltekit-superforms
: مكتبة النماذج الفائقة الشحن لـ Sveltekit مع التحقق من صحة ZOD.mobx-zod-form
: منشئ النماذج الأولى للبيانات يعتمد على Mobx & Zod.@vee-validate/zod
: Form Library لـ Vue.js مع التحقق من صحة مخطط ZOD.zod-form-renderer
: حقول النماذج التلقائية من مخطط ZOD وجعلها مع شكل رد فعل مع سلامة نوع E2E. zod-to-ts
: قم بإنشاء تعريفات TypeScript من مخططات ZOD.zod-to-json-schema
: قم بتحويل مخططات Zod إلى مخططات JSON.@anatine/zod-openapi
: يحول مخطط Zod إلى SchemaObject
Openapi v3.x.zod-fast-check
: Generate fast-check
arbitraries from Zod schemas.zod-dto
: إنشاء Nest.js DTOs من مخطط Zod.fastify-type-provider-zod
: إنشاء مقدمي نوع Fastify من Schemas.zod-to-openapi
: قم بإنشاء مستندات OpenAPI (Swagger) الكاملة من ZOD ، بما في ذلك المخططات ، ونقاط النهاية والمعلمات.nestjs-graphql-zod
: يولد فئات نموذج NESTJS GRATEQL من مخططات ZOD. يوفر Decorators GraphQL يعملون مع مخططات Zod.zod-openapi
: إنشاء وثائق Openapi V3.x كاملة من Schemas Zod.fastify-zod-openapi
: مزود نوع Fastify ، التحقق من الصحة ، التسلسل ودعم @Fastify/Swagger لمخططات ZOD.typeschema
: محول عالمي للتحقق من المخطط.zodex
: (DE) التسلسلية لمخططات zod ts-to-zod
: تحويل تعريفات TypeScript إلى مخططات ZOD.@runtyping/zod
: قم بإنشاء Zod من أنواع ثابتة ومخطط JSON.json-schema-to-zod
: قم بتحويل مخططات JSON الخاصة بك إلى مخططات ZOD. عرض حي.json-to-zod
: تحويل كائنات JSON إلى مخططات ZOD. Live demo.graphql-codegen-typescript-validation-schema
: Plugin GraphQL Code Generator لإنشاء مخطط التحقق من صحة النماذج من مخطط GraphQL الخاص بك.zod-prisma
: قم بإنشاء مخططات Zod من مخطط Prisma الخاص بك.Supervillain
: قم بإنشاء مخططات Zod من بنيتك GO.prisma-zod-generator
: Sele Zod Schemas من مخطط Prisma الخاص بك.drizzle-zod
: تنبعث من مخططات zod من مخطط الرذاذ الخاص بك.prisma-trpc-generator
: تنفذ جهاز التوجيه TRPC تم تنفيذه بالكامل ومخططات التحقق من الصحة باستخدام ZOD.zod-prisma-types
بإنشاء أنواع Zod من نماذج Prisma الخاصة بك.quicktype
: تحويل كائنات JSON ومخططات JSON إلى مخططات ZOD.@sanity-typed/zod
: قم بإنشاء مخططات zod من مخططات العقل.java-to-zod
: تحويل Pojos إلى مخططات ZodOrval
: قم بإنشاء مخططات Zod من مخططات OpenapiKubb
: قم بإنشاء مخططات SDKs و ZOD من مخططات Openapi الخاصة بك @anatine/zod-mock
: إنشاء بيانات وهمية من مخطط ZOD. مدعوم من faker.js.zod-mocking
: قم بإنشاء بيانات وهمية من مخططات ZOD الخاصة بك.zod-fixture
: استخدم مخططات Zod الخاصة بك لأتمتة توليد تركيبات الاختبار غير ذات الصلة بطريقة حتمية.zocker
: توليد بيانات وهمية معقولة من مخططاتك.zodock
إنشاء بيانات وهمية على أساس مخططات ZOD.zod-schema-faker
بإنشاء بيانات وهمية من مخططات ZOD. Powered by @faker-js/faker and randexp.js freerstore
: Firestore Cost Optimizer.slonik
: Node.js Postgres Client مع تكامل ZOD قوي.schemql
: يعزز سير عمل SQL الخاص بك من خلال الجمع بين SQL الخام مع السلامة من النوع المستهدف والتحقق من المخطط.soly
: إنشاء تطبيقات CLI مع ZOD.pastel
: إنشاء تطبيقات CLI مع React و Zod و Enk.zod-xlsx
: مصلحة الموارد القائمة على XLSX باستخدام مخططات ZOD.znv
: تحليل البيئة الآمنة من النوع والتحقق من صحة Node.js مع مخططات ZOD.zod-config
: تحميل تكوينات عبر مصادر متعددة ذات محولات مرنة ، مما يضمن سلامة النوع مع ZOD.unplugin-environment
: مكون إضافي لتحميل متغيرات ENVIROMENT بأمان مع التحقق من صحة المخطط ، بسيطة مع الوحدة الافتراضية ، وأمان النوع مع Intellisense ، و DX أفضل؟ مدعوم من Zod. zod_utilz
: المرافق اللاإرادية للإطار ل zod.zod-playground
: أداة لتعلم واختبار وظائف التحقق من صحة مخطط ZOD. وصلة.zod-sandbox
: بيئة مسيطر عليها لاختبار مخططات zod. عرض حي.zod-dev
: يعطل مشروط وقت تشغيل وقت تشغيل Zod في الإنتاج.zod-accelerator
: تسريع إنتاجية Zod حتى 100x. TypeScript 4.5+!
يجب تمكين الوضع strict
في tsconfig.json
. هذه أفضل الممارسات لجميع مشاريع TypeScript.
// tsconfig.json
{
// ...
"compilerOptions" : {
// ...
"strict" : true
}
}
npm
(عقدة/كعكة) npm install zod # npm
yarn add zod # yarn
bun add zod # bun
pnpm add zod # pnpm
Zod also publishes a canary version on every commit. لتثبيت الكناري:
npm install zod@canary # npm
yarn add zod@canary # yarn
bun add zod@canary # bun
pnpm add zod@canary # pnpm
deno.land/x
(Deno)على عكس العقدة ، يعتمد Deno على واردات URL المباشرة بدلاً من مدير الحزمة مثل NPM. Zod متاح على deno.land/x. يمكن استيراد أحدث إصدار مثل:
import { z } from "https://deno.land/x/zod/mod.ts" ;
يمكنك أيضًا تحديد نسخة معينة:
import { z } from "https://deno.land/x/[email protected]/mod.ts" ;
يفترض بقية هذه القراءة أنك تستخدم NPM والاستيراد مباشرة من حزمة
"zod"
.
إنشاء مخطط سلسلة بسيط
import { z } from "zod" ;
// creating a schema for strings
const mySchema = z . string ( ) ;
// parsing
mySchema . parse ( "tuna" ) ; // => "tuna"
mySchema . parse ( 12 ) ; // => throws ZodError
// "safe" parsing (doesn't throw error if validation fails)
mySchema . safeParse ( "tuna" ) ; // => { success: true; data: "tuna" }
mySchema . safeParse ( 12 ) ; // => { success: false; error: ZodError }
إنشاء مخطط كائن
import { z } from "zod" ;
const User = z . object ( {
username : z . string ( ) ,
} ) ;
User . parse ( { username : "Ludwig" } ) ;
// extract the inferred type
type User = z . infer < typeof User > ;
// { username: string }
import { z } from "zod" ;
// primitive values
z . string ( ) ;
z . number ( ) ;
z . bigint ( ) ;
z . boolean ( ) ;
z . date ( ) ;
z . symbol ( ) ;
// empty types
z . undefined ( ) ;
z . null ( ) ;
z . void ( ) ; // accepts undefined
// catch-all types
// allows any value
z . any ( ) ;
z . unknown ( ) ;
// never type
// allows no values
z . never ( ) ;
يوفر Zod الآن طريقة أكثر ملاءمة لإجبار القيم البدائية.
const schema = z . coerce . string ( ) ;
schema . parse ( "tuna" ) ; // => "tuna"
schema . parse ( 12 ) ; // => "12"
أثناء خطوة التحليل ، يتم تمرير الإدخال من خلال وظيفة String()
، وهي عبارة عن JavaScript مدمج لإكراه البيانات على السلاسل.
schema . parse ( 12 ) ; // => "12"
schema . parse ( true ) ; // => "true"
schema . parse ( undefined ) ; // => "undefined"
schema . parse ( null ) ; // => "null"
المخطط الذي تم إرجاعه هو مثيل ZodString
عادي حتى تتمكن من استخدام جميع طرق السلسلة.
z . coerce . string ( ) . email ( ) . min ( 5 ) ;
كيف يعمل الإكراه
All primitive types support coercion. ZOD إكراه جميع المدخلات باستخدام المُنشئات المدمجة: String(input)
، Number(input)
، new Date(input)
، إلخ.
z . coerce . string ( ) ; // String(input)
z . coerce . number ( ) ; // Number(input)
z . coerce . boolean ( ) ; // Boolean(input)
z . coerce . bigint ( ) ; // BigInt(input)
z . coerce . date ( ) ; // new Date(input)
ملاحظة - قد لا تعمل الإكراه المنطقي مع z.coerce.boolean()
على الطريقة التي تتوقعها. أي قيمة للحقيقة يتم إكراه على true
، وأي قيمة falsy يتم إجبارها على false
.
const schema = z . coerce . boolean ( ) ; // Boolean(input)
schema . parse ( "tuna" ) ; // => true
schema . parse ( "true" ) ; // => true
schema . parse ( "false" ) ; // => true
schema . parse ( 1 ) ; // => true
schema . parse ( [ ] ) ; // => true
schema . parse ( 0 ) ; // => false
schema . parse ( "" ) ; // => false
schema . parse ( undefined ) ; // => false
schema . parse ( null ) ; // => false
لمزيد من التحكم في منطق الإكراه ، فكر في استخدام z.preprocess
أو z.pipe()
.
تمثل المخططات الحرفية نوعًا حرفيًا ، مثل "hello world"
أو 5
.
const tuna = z . literal ( "tuna" ) ;
const twelve = z . literal ( 12 ) ;
const twobig = z . literal ( 2n ) ; // bigint literal
const tru = z . literal ( true ) ;
const terrificSymbol = Symbol ( "terrific" ) ;
const terrific = z . literal ( terrificSymbol ) ;
// retrieve literal value
tuna . value ; // "tuna"
لا يوجد حاليا أي دعم للحرفية التاريخ في Zod. إذا كان لديك حالة استخدام لهذه الميزة ، فيرجى تقديم مشكلة.
يتضمن ZOD حفنة من عمليات التحقق الخاصة بالسلسلة.
// validations
z . string ( ) . max ( 5 ) ;
z . string ( ) . min ( 5 ) ;
z . string ( ) . length ( 5 ) ;
z . string ( ) . email ( ) ;
z . string ( ) . url ( ) ;
z . string ( ) . emoji ( ) ;
z . string ( ) . uuid ( ) ;
z . string ( ) . nanoid ( ) ;
z . string ( ) . cuid ( ) ;
z . string ( ) . cuid2 ( ) ;
z . string ( ) . ulid ( ) ;
z . string ( ) . regex ( regex ) ;
z . string ( ) . includes ( string ) ;
z . string ( ) . startsWith ( string ) ;
z . string ( ) . endsWith ( string ) ;
z . string ( ) . datetime ( ) ; // ISO 8601; by default only `Z` timezone allowed
z . string ( ) . ip ( ) ; // defaults to allow both IPv4 and IPv6
// transforms
z . string ( ) . trim ( ) ; // trim whitespace
z . string ( ) . toLowerCase ( ) ; // toLowerCase
z . string ( ) . toUpperCase ( ) ; // toUpperCase
// added in Zod 3.23
z . string ( ) . date ( ) ; // ISO date format (YYYY-MM-DD)
z . string ( ) . time ( ) ; // ISO time format (HH:mm:ss[.SSSSSS])
z . string ( ) . duration ( ) ; // ISO 8601 duration
z . string ( ) . base64 ( ) ;
تحقق من Veadator.js للحصول على مجموعة من وظائف التحقق من صحة السلسلة المفيدة الأخرى التي يمكن استخدامها بالاقتران مع التحسينات.
يمكنك تخصيص بعض رسائل الخطأ الشائعة عند إنشاء مخطط سلسلة.
const name = z . string ( {
required_error : "Name is required" ,
invalid_type_error : "Name must be a string" ,
} ) ;
عند استخدام طرق التحقق من الصحة ، يمكنك تمرير وسيطة إضافية لتوفير رسالة خطأ مخصصة.
z . string ( ) . min ( 5 , { message : "Must be 5 or more characters long" } ) ;
z . string ( ) . max ( 5 , { message : "Must be 5 or fewer characters long" } ) ;
z . string ( ) . length ( 5 , { message : "Must be exactly 5 characters long" } ) ;
z . string ( ) . email ( { message : "Invalid email address" } ) ;
z . string ( ) . url ( { message : "Invalid url" } ) ;
z . string ( ) . emoji ( { message : "Contains non-emoji characters" } ) ;
z . string ( ) . uuid ( { message : "Invalid UUID" } ) ;
z . string ( ) . includes ( "tuna" , { message : "Must include tuna" } ) ;
z . string ( ) . startsWith ( "https://" , { message : "Must provide secure URL" } ) ;
z . string ( ) . endsWith ( ".com" , { message : "Only .com domains allowed" } ) ;
z . string ( ) . datetime ( { message : "Invalid datetime string! Must be UTC." } ) ;
z . string ( ) . date ( { message : "Invalid date string!" } ) ;
z . string ( ) . time ( { message : "Invalid time string!" } ) ;
z . string ( ) . ip ( { message : "Invalid IP address" } ) ;
كما لاحظت ، تتضمن Zod String بعض عمليات التحقق من تاريخ/الوقت. تعتمد عمليات التحقق هذه على تعبير منتظم ، لذا فهي ليست صارمة مثل مكتبة التاريخ/الوقت الكاملة. ومع ذلك ، فهي مريحة للغاية للتحقق من مدخلات المستخدم.
طريقة z.string().datetime()
تفرض ISO 8601 ؛ default is no timezone offsets and arbitrary sub-second decimal precision.
const datetime = z . string ( ) . datetime ( ) ;
datetime . parse ( "2020-01-01T00:00:00Z" ) ; // pass
datetime . parse ( "2020-01-01T00:00:00.123Z" ) ; // pass
datetime . parse ( "2020-01-01T00:00:00.123456Z" ) ; // pass (arbitrary precision)
datetime . parse ( "2020-01-01T00:00:00+02:00" ) ; // fail (no offsets allowed)
يمكن السماح بتعويضات المنطقة الزمنية عن طريق تعيين خيار offset
إلى true
.
const datetime = z . string ( ) . datetime ( { offset : true } ) ;
datetime . parse ( "2020-01-01T00:00:00+02:00" ) ; // pass
datetime . parse ( "2020-01-01T00:00:00.123+02:00" ) ; // pass (millis optional)
datetime . parse ( "2020-01-01T00:00:00.123+0200" ) ; // pass (millis optional)
datetime . parse ( "2020-01-01T00:00:00.123+02" ) ; // pass (only offset hours)
datetime . parse ( "2020-01-01T00:00:00Z" ) ; // pass (Z still supported)
يمكنك بالإضافة إلى ذلك تقييد precision
المسموح بها. بشكل افتراضي ، يتم دعم الدقة الفرعية الفرعية التعسفية (ولكن اختياري).
const datetime = z . string ( ) . datetime ( { precision : 3 } ) ;
datetime . parse ( "2020-01-01T00:00:00.123Z" ) ; // pass
datetime . parse ( "2020-01-01T00:00:00Z" ) ; // fail
datetime . parse ( "2020-01-01T00:00:00.123456Z" ) ; // fail
تمت إضافة في Zod 3.23
تقوم طريقة z.string().date()
بالتحقق من الأوتار في التنسيق YYYY-MM-DD
.
const date = z . string ( ) . date ( ) ;
date . parse ( "2020-01-01" ) ; // pass
date . parse ( "2020-1-1" ) ; // fail
date . parse ( "2020-01-32" ) ; // fail
تمت إضافة في Zod 3.23
تقوم طريقة z.string().time()
بالتحقق من الأوتار في التنسيق HH:MM:SS[.s+]
. والثانية يمكن أن تشمل الدقة العشرية التعسفية. لا يتيح إزاحة المنطقة الزمنية من أي نوع.
const time = z . string ( ) . time ( ) ;
time . parse ( "00:00:00" ) ; // pass
time . parse ( "09:52:31" ) ; // pass
time . parse ( "23:59:59.9999999" ) ; // pass (arbitrary precision)
time . parse ( "00:00:00.123Z" ) ; // fail (no `Z` allowed)
time . parse ( "00:00:00.123+02:00" ) ; // fail (no offsets allowed)
يمكنك تعيين خيار precision
لتقييد الدقة العشرية المسموح بها.
const time = z . string ( ) . time ( { precision : 3 } ) ;
time . parse ( "00:00:00.123" ) ; // pass
time . parse ( "00:00:00.123456" ) ; // fail
time . parse ( "00:00:00" ) ; // fail
طريقة z.string().ip()
بشكل افتراضي التحقق من صحة IPv4 و IPv6.
const ip = z . string ( ) . ip ( ) ;
ip . parse ( "192.168.1.1" ) ; // pass
ip . parse ( "84d5:51a0:9114:1855:4cfa:f2d7:1f12:7003" ) ; // pass
ip . parse ( "84d5:51a0:9114:1855:4cfa:f2d7:1f12:192.168.1.1" ) ; // pass
ip . parse ( "256.1.1.1" ) ; // fail
ip . parse ( "84d5:51a0:9114:gggg:4cfa:f2d7:1f12:7003" ) ; // fail
يمكنك أيضًا تعيين version
IP.
const ipv4 = z . string ( ) . ip ( { version : "v4" } ) ;
ipv4 . parse ( "84d5:51a0:9114:1855:4cfa:f2d7:1f12:7003" ) ; // fail
const ipv6 = z . string ( ) . ip ( { version : "v6" } ) ;
ipv6 . parse ( "192.168.1.1" ) ; // fail
يمكنك تخصيص رسائل خطأ معينة عند إنشاء مخطط الأرقام.
const age = z . number ( {
required_error : "Age is required" ,
invalid_type_error : "Age must be a number" ,
} ) ;
يتضمن ZOD حفنة من عمليات التحقق من الأرقام الخاصة.
z . number ( ) . gt ( 5 ) ;
z . number ( ) . gte ( 5 ) ; // alias .min(5)
z . number ( ) . lt ( 5 ) ;
z . number ( ) . lte ( 5 ) ; // alias .max(5)
z . number ( ) . int ( ) ; // value must be an integer
z . number ( ) . positive ( ) ; // > 0
z . number ( ) . nonnegative ( ) ; // >= 0
z . number ( ) . negative ( ) ; // < 0
z . number ( ) . nonpositive ( ) ; // <= 0
z . number ( ) . multipleOf ( 5 ) ; // Evenly divisible by 5. Alias .step(5)
z . number ( ) . finite ( ) ; // value must be finite, not Infinity or -Infinity
z . number ( ) . safe ( ) ; // value must be between Number.MIN_SAFE_INTEGER and Number.MAX_SAFE_INTEGER
اختياريا ، يمكنك تمرير الوسيطة الثانية لتوفير رسالة خطأ مخصصة.
z . number ( ) . lte ( 5 , { message : "this?is?too?big" } ) ;
يتضمن ZOD حفنة من عمليات التحقق من Bigint الخاصة.
z . bigint ( ) . gt ( 5n ) ;
z . bigint ( ) . gte ( 5n ) ; // alias `.min(5n)`
z . bigint ( ) . lt ( 5n ) ;
z . bigint ( ) . lte ( 5n ) ; // alias `.max(5n)`
z . bigint ( ) . positive ( ) ; // > 0n
z . bigint ( ) . nonnegative ( ) ; // >= 0n
z . bigint ( ) . negative ( ) ; // < 0n
z . bigint ( ) . nonpositive ( ) ; // <= 0n
z . bigint ( ) . multipleOf ( 5n ) ; // Evenly divisible by 5n.
يمكنك تخصيص رسائل خطأ معينة عند إنشاء مخطط نان.
const isNaN = z . nan ( {
required_error : "isNaN is required" ,
invalid_type_error : "isNaN must be 'not a number'" ,
} ) ;
يمكنك تخصيص رسائل خطأ معينة عند إنشاء مخطط منطقي.
const isActive = z . boolean ( {
required_error : "isActive is required" ,
invalid_type_error : "isActive must be a boolean" ,
} ) ;
استخدم Z.Date () للتحقق من صحة مثيلات Date
.
z . date ( ) . safeParse ( new Date ( ) ) ; // success: true
z . date ( ) . safeParse ( "2022-01-12T00:00:00.000Z" ) ; // success: false
يمكنك تخصيص رسائل خطأ معينة عند إنشاء مخطط تاريخ.
const myDateSchema = z . date ( {
required_error : "Please select a date and time" ,
invalid_type_error : "That's not a date!" ,
} ) ;
Zod provides a handful of date-specific validations.
z . date ( ) . min ( new Date ( "1900-01-01" ) , { message : "Too old" } ) ;
z . date ( ) . max ( new Date ( ) , { message : "Too young!" } ) ;
الإكراه حتى الآن
منذ zod 3.20 ، استخدم z.coerce.date()
لتمرير الإدخال خلال new Date(input)
.
const dateSchema = z . coerce . date ( ) ;
type DateSchema = z . infer < typeof dateSchema > ;
// type DateSchema = Date
/* valid dates */
console . log ( dateSchema . safeParse ( "2023-01-10T00:00:00.000Z" ) . success ) ; // true
console . log ( dateSchema . safeParse ( "2023-01-10" ) . success ) ; // true
console . log ( dateSchema . safeParse ( "1/10/23" ) . success ) ; // true
console . log ( dateSchema . safeParse ( new Date ( "1/10/23" ) ) . success ) ; // true
/* invalid dates */
console . log ( dateSchema . safeParse ( "2023-13-10" ) . success ) ; // false
console . log ( dateSchema . safeParse ( "0000-00-00" ) . success ) ; // false
بالنسبة لإصدارات ZOD الأقدم ، استخدم z.preprocess
كما هو موضح في هذا الموضوع.
const FishEnum = z . enum ( [ "Salmon" , "Tuna" , "Trout" ] ) ;
type FishEnum = z . infer < typeof FishEnum > ;
// 'Salmon' | 'Tuna' | 'Trout'
z.enum
هي طريقة غير أصلية لإعلان مخطط مع مجموعة ثابتة من قيم السلسلة المسموح بها. تمرير مجموعة القيم مباشرة إلى z.enum()
. بدلاً من ذلك ، استخدم as const
لتحديد قيم التعداد الخاصة بك باعتبارها مجموعة من الأوتار. انظر مستندات تأكيد كونستور للحصول على التفاصيل.
const VALUES = [ "Salmon" , "Tuna" , "Trout" ] as const ;
const FishEnum = z . enum ( VALUES ) ;
هذا غير مسموح به ، لأن Zod غير قادر على استنتاج القيم الدقيقة لكل عنصر.
const fish = [ "Salmon" , "Tuna" , "Trout" ] ;
const FishEnum = z . enum ( fish ) ;
.enum
للحصول على الإكمال التلقائي مع تعداد zod ، استخدم خاصية .enum
لمخططك:
FishEnum . enum . Salmon ; // => autocompletes
FishEnum . enum ;
/*
=> {
Salmon: "Salmon",
Tuna: "Tuna",
Trout: "Trout",
}
*/
يمكنك أيضًا استرداد قائمة الخيارات باعتبارها tuple مع خاصية .options
:
FishEnum . options ; // ["Salmon", "Tuna", "Trout"];
.exclude/.extract()
يمكنك إنشاء مجموعات فرعية من zod enum مع أساليب .exclude
و .extract
.
const FishEnum = z . enum ( [ "Salmon" , "Tuna" , "Trout" ] ) ;
const SalmonAndTrout = FishEnum . extract ( [ "Salmon" , "Trout" ] ) ;
const TunaOnly = FishEnum . exclude ( [ "Salmon" , "Trout" ] ) ;
تعداد Zod هي النهج الموصى به لتحديد التعدادات والتحقق منها. ولكن إذا كنت بحاجة إلى التحقق من صحة ضد التعداد من مكتبة طرف ثالث (أو كنت لا ترغب في إعادة كتابة التعدادات الموجودة لديك) ، فيمكنك استخدام z.nativeEnum()
.
التعداد الرقمي
enum Fruits {
Apple ,
Banana ,
}
const FruitEnum = z . nativeEnum ( Fruits ) ;
type FruitEnum = z . infer < typeof FruitEnum > ; // Fruits
FruitEnum . parse ( Fruits . Apple ) ; // passes
FruitEnum . parse ( Fruits . Banana ) ; // passes
FruitEnum . parse ( 0 ) ; // passes
FruitEnum . parse ( 1 ) ; // passes
FruitEnum . parse ( 3 ) ; // fails
تعداد السلسلة
enum Fruits {
Apple = "apple" ,
Banana = "banana" ,
Cantaloupe , // you can mix numerical and string enums
}
const FruitEnum = z . nativeEnum ( Fruits ) ;
type FruitEnum = z . infer < typeof FruitEnum > ; // Fruits
FruitEnum . parse ( Fruits . Apple ) ; // passes
FruitEnum . parse ( Fruits . Cantaloupe ) ; // passes
FruitEnum . parse ( "apple" ) ; // passes
FruitEnum . parse ( "banana" ) ; // passes
FruitEnum . parse ( 0 ) ; // passes
FruitEnum . parse ( "Cantaloupe" ) ; // fails
التعدادات
The .nativeEnum()
function works for as const
objects as well.as const
requires TypeScript 3.4+!
const Fruits = {
Apple : "apple" ,
Banana : "banana" ,
Cantaloupe : 3 ,
} as const ;
const FruitEnum = z . nativeEnum ( Fruits ) ;
type FruitEnum = z . infer < typeof FruitEnum > ; // "apple" | "banana" | 3
FruitEnum . parse ( "apple" ) ; // passes
FruitEnum . parse ( "banana" ) ; // passes
FruitEnum . parse ( 3 ) ; // passes
FruitEnum . parse ( "Cantaloupe" ) ; // fails
يمكنك الوصول إلى الكائن الأساسي باستخدام خاصية .enum
:
FruitEnum . enum . Apple ; // "apple"
يمكنك جعل أي مخطط اختياري مع z.optional()
. هذا يلتف المخطط في مثيل ZodOptional
ويعيد النتيجة.
const schema = z . optional ( z . string ( ) ) ;
schema . parse ( undefined ) ; // => returns undefined
type A = z . infer < typeof schema > ; // string | undefined
للراحة ، يمكنك أيضًا استدعاء طريقة .optional()
على مخطط موجود.
const user = z . object ( {
username : z . string ( ) . optional ( ) ,
} ) ;
type C = z . infer < typeof user > ; // { username?: string | undefined };
يمكنك استخراج المخطط ملفوف من مثيل ZodOptional
مع .unwrap()
.
const stringSchema = z . string ( ) ;
const optionalString = stringSchema . optional ( ) ;
optionalString . unwrap ( ) === stringSchema ; // true
وبالمثل ، يمكنك إنشاء أنواع لاغية مع z.nullable()
.
const nullableString = z . nullable ( z . string ( ) ) ;
nullableString . parse ( "asdf" ) ; // => "asdf"
nullableString . parse ( null ) ; // => null
أو استخدم طريقة .nullable()
.
const E = z . string ( ) . nullable ( ) ; // equivalent to nullableString
type E = z . infer < typeof E > ; // string | null
استخراج المخطط الداخلي مع .unwrap()
.
const stringSchema = z . string ( ) ;
const nullableString = stringSchema . nullable ( ) ;
nullableString . unwrap ( ) === stringSchema ; // true
// all properties are required by default
const Dog = z . object ( {
name : z . string ( ) ,
age : z . number ( ) ,
} ) ;
// extract the inferred type like this
type Dog = z . infer < typeof Dog > ;
// equivalent to:
type Dog = {
name : string ;
age : number ;
} ;
.shape
استخدم .shape
للوصول إلى المخططات لمفتاح معين.
Dog . shape . name ; // => string schema
Dog . shape . age ; // => number schema
.keyof
use .keyof
لإنشاء مخطط ZodEnum
من مفاتيح مخطط الكائن.
const keySchema = Dog . keyof ( ) ;
keySchema ; // ZodEnum<["name", "age"]>
.extend
يمكنك إضافة حقول إضافية إلى مخطط كائن مع طريقة .extend
.
const DogWithBreed = Dog . extend ( {
breed : z . string ( ) ,
} ) ;
يمكنك استخدام .extend
للكتابة فوق الحقول! كن حذرا مع هذه القوة!
.merge
أي ما يعادل A.extend(B.shape)
.
const BaseTeacher = z . object ( { students : z . array ( z . string ( ) ) } ) ;
const HasID = z . object ( { id : z . string ( ) } ) ;
const Teacher = BaseTeacher . merge ( HasID ) ;
type Teacher = z . infer < typeof Teacher > ; // => { students: string[], id: string }
إذا كانت المخططين يشتركان في مفاتيح ، فإن خصائص B تتجاوز خاصية A. المخطط الذي تم إرجاعه يرث أيضًا سياسة "غير معروفة" (الشريط/الصارم/النجاح) ومخطط Catchall لـ B.
.pick/.omit
مستوحاة من أنواع الأداة المساعدة Pick
من TypeScript و Omit
، تحتوي جميع مخططات كائن ZOD على طرق .pick
و .omit
التي تُرجع نسخة معدلة. النظر في مخطط الوصفة هذا:
const Recipe = z . object ( {
id : z . string ( ) ,
name : z . string ( ) ,
ingredients : z . array ( z . string ( ) ) ,
} ) ;
للحفاظ على مفاتيح معينة فقط ، استخدم .pick
.
const JustTheName = Recipe . pick ( { name : true } ) ;
type JustTheName = z . infer < typeof JustTheName > ;
// => { name: string }
لإزالة بعض المفاتيح ، استخدم .omit
.
const NoIDRecipe = Recipe . omit ( { id : true } ) ;
type NoIDRecipe = z . infer < typeof NoIDRecipe > ;
// => { name: string, ingredients: string[] }
.partial
Inspired by the built-in TypeScript utility type Partial, the .partial
method makes all properties optional.
بدءًا من هذا الكائن:
const user = z . object ( {
email : z . string ( ) ,
username : z . string ( ) ,
} ) ;
// { email: string; username: string }
يمكننا إنشاء نسخة جزئية:
const partialUser = user . partial ( ) ;
// { email?: string | undefined; username?: string | undefined }
يمكنك أيضًا تحديد الخصائص التي يجب جعلها اختيارية:
const optionalEmail = user . partial ( {
email : true ,
} ) ;
/*
{
email?: string | undefined;
username: string
}
*/
.deepPartial
.partial
. هناك أيضًا نسخة "عميقة":
const user = z . object ( {
username : z . string ( ) ,
location : z . object ( {
latitude : z . number ( ) ,
longitude : z . number ( ) ,
} ) ,
strings : z . array ( z . object ( { value : z . string ( ) } ) ) ,
} ) ;
const deepPartialUser = user . deepPartial ( ) ;
/*
{
username?: string | undefined,
location?: {
latitude?: number | undefined;
longitude?: number | undefined;
} | undefined,
strings?: { value?: string}[]
}
*/
القيد المهم: الجزئية العميقة تعمل فقط كما هو متوقع في التسلسلات الهرمية للأشياء والصفائف والطول.
.required
على .required
.partial
.
بدءًا من هذا الكائن:
const user = z
. object ( {
email : z . string ( ) ,
username : z . string ( ) ,
} )
. partial ( ) ;
// { email?: string | undefined; username?: string | undefined }
يمكننا إنشاء نسخة مطلوبة:
const requiredUser = user . required ( ) ;
// { email: string; username: string }
يمكنك أيضًا تحديد الخصائص المطلوبة:
const requiredEmail = user . required ( {
email : true ,
} ) ;
/*
{
email: string;
username?: string | undefined;
}
*/
.passthrough
بشكل افتراضي ، قم بتجريد مخططات كائن ZOD من المفاتيح غير المعترف بها أثناء التحليل.
const person = z . object ( {
name : z . string ( ) ,
} ) ;
person . parse ( {
name : "bob dylan" ,
extraKey : 61 ,
} ) ;
// => { name: "bob dylan" }
// extraKey has been stripped
بدلاً من ذلك ، إذا كنت ترغب في المرور عبر مفاتيح غير معروفة ، فاستخدم .passthrough()
.
person . passthrough ( ) . parse ( {
name : "bob dylan" ,
extraKey : 61 ,
} ) ;
// => { name: "bob dylan", extraKey: 61 }
.strict
بشكل افتراضي ، قم بتجريد مخططات كائن ZOD من المفاتيح غير المعترف بها أثناء التحليل. يمكنك عدم السماح بمفاتيح غير معروفة باستخدام .strict()
. إذا كان هناك أي مفاتيح غير معروفة في المدخلات ، فسيقوم Zod بإلقاء خطأ.
const person = z
. object ( {
name : z . string ( ) ,
} )
. strict ( ) ;
person . parse ( {
name : "bob dylan" ,
extraKey : 61 ,
} ) ;
// => throws ZodError
.strip
يمكنك استخدام طريقة .strip
لإعادة تعيين مخطط كائن إلى السلوك الافتراضي (تجريد المفاتيح غير المعترف بها).
.catchall
يمكنك تمرير مخطط "catchall" إلى مخطط كائن. سيتم التحقق من صحة جميع المفاتيح غير المعروفة ضدها.
const person = z
. object ( {
name : z . string ( ) ,
} )
. catchall ( z . number ( ) ) ;
person . parse ( {
name : "bob dylan" ,
validExtraKey : 61 , // works fine
} ) ;
person . parse ( {
name : "bob dylan" ,
validExtraKey : false , // fails
} ) ;
// => throws ZodError
باستخدام .catchall()
obviates .passthrough()
، .strip()
، أو .strict()
. تعتبر جميع المفاتيح الآن "معروفة".
const stringArray = z . array ( z . string ( ) ) ;
// equivalent
const stringArray = z . string ( ) . array ( ) ;
كن حذرا مع طريقة .array()
. يعيد مثيل ZodArray
جديد. هذا يعني الترتيب الذي تتصل به أساليب الأمور. على سبيل المثال:
z . string ( ) . optional ( ) . array ( ) ; // (string | undefined)[]
z . string ( ) . array ( ) . optional ( ) ; // string[] | undefined
.element
استخدام .element
للوصول إلى المخطط لعنصر من الصفيف.
stringArray . element ; // => string schema
.nonempty
إذا كنت ترغب في التأكد من أن الصفيف يحتوي على عنصر واحد على الأقل ، فاستخدم .nonempty()
.
const nonEmptyStrings = z . string ( ) . array ( ) . nonempty ( ) ;
// the inferred type is now
// [string, ...string[]]
nonEmptyStrings . parse ( [ ] ) ; // throws: "Array cannot be empty"
nonEmptyStrings . parse ( [ "Ariana Grande" ] ) ; // passes
يمكنك اختياريا تحديد رسالة خطأ مخصصة:
// optional custom error message
const nonEmptyStrings = z . string ( ) . array ( ) . nonempty ( {
message : "Can't be empty!" ,
} ) ;
.min/.max/.length
z . string ( ) . array ( ) . min ( 5 ) ; // must contain 5 or more items
z . string ( ) . array ( ) . max ( 5 ) ; // must contain 5 or fewer items
z . string ( ) . array ( ) . length ( 5 ) ; // must contain 5 items exactly
على عكس .nonempty()
هذه الطرق لا تغير النوع المستنتج.
على عكس المصفوفات ، يحتوي Tuples على عدد ثابت من العناصر ويمكن أن يكون لكل عنصر نوع مختلف.
const athleteSchema = z . tuple ( [
z . string ( ) , // name
z . number ( ) , // jersey number
z . object ( {
pointsScored : z . number ( ) ,
} ) , // statistics
] ) ;
type Athlete = z . infer < typeof athleteSchema > ;
// type Athlete = [string, number, { pointsScored: number }]
يمكن إضافة وسيطة variadic ("REST") باستخدام طريقة .rest
.
const variadicTuple = z . tuple ( [ z . string ( ) ] ) . rest ( z . number ( ) ) ;
const result = variadicTuple . parse ( [ "hello" , 1 , 2 , 3 ] ) ;
// => [string, ...number[]];
يتضمن ZOD طريقة z.union
مدمجة لتأليف "أو".
const stringOrNumber = z . union ( [ z . string ( ) , z . number ( ) ] ) ;
stringOrNumber . parse ( "foo" ) ; // passes
stringOrNumber . parse ( 14 ) ; // passes
سيقوم ZOD باختبار المدخلات مقابل كل من "الخيارات" بالترتيب وإرجاع القيمة الأولى التي تتحقق بنجاح.
للراحة ، يمكنك أيضًا استخدام طريقة .or
:
const stringOrNumber = z . string ( ) . or ( z . number ( ) ) ;
التحقق من صحة السلسلة الاختيارية:
للتحقق من صحة إدخال النموذج الاختياري ، يمكنك اتحاد التحقق من صحة السلسلة المطلوبة بسلسلة فارغة حرفية.
يقوم هذا المثال بالتحقق من صحة الإدخال اختياريًا ولكنه يحتاج إلى احتواء عنوان URL صالح:
const optionalUrl = z . union ( [ z . string ( ) . url ( ) . nullish ( ) , z . literal ( "" ) ] ) ;
console . log ( optionalUrl . safeParse ( undefined ) . success ) ; // true
console . log ( optionalUrl . safeParse ( null ) . success ) ; // true
console . log ( optionalUrl . safeParse ( "" ) . success ) ; // true
console . log ( optionalUrl . safeParse ( "https://zod.dev" ) . success ) ; // true
console . log ( optionalUrl . safeParse ( "not a valid url" ) . success ) ; // false
الاتحاد التمييزي هو اتحاد مخططات الكائنات التي تشترك جميعها في مفتاح معين.
type MyUnion =
| { status : "success" ; data : string }
| { status : "failed" ; error : Error } ;
يمكن تمثيل هذه النقابات باستخدام طريقة z.discriminatedUnion
. يتيح ذلك التقييم بشكل أسرع ، لأن ZOD يمكنه التحقق من مفتاح التمييز ( status
في المثال أعلاه) لتحديد المخطط الذي يجب استخدامه لتحليل المدخلات. هذا يجعل تحليل أكثر كفاءة ويتيح لـ Zod الإبلاغ عن أخطاء ودية.
باستخدام طريقة الاتحاد الأساسية ، يتم اختبار الإدخال مقابل كل من "الخيارات" المقدمة ، وفي حالة البطلان ، يتم عرض المشكلات لجميع "الخيارات" في خطأ ZOD. من ناحية أخرى ، يسمح الاتحاد التمييز لاختيار واحد فقط من "الخيارات" ، واختبارها ضدها ، وإظهار القضايا المتعلقة بهذا "الخيار" فقط.
const myUnion = z . discriminatedUnion ( "status" , [
z . object ( { status : z . literal ( "success" ) , data : z . string ( ) } ) ,
z . object ( { status : z . literal ( "failed" ) , error : z . instanceof ( Error ) } ) ,
] ) ;
myUnion . parse ( { status : "success" , data : "yippie ki yay" } ) ;
يمكنك استخراج إشارة إلى مجموعة من المخططات مع خاصية .options
.
myUnion . options ; // [ZodObject<...>, ZodObject<...>]
لدمج اثنين أو أكثر من النقابات التمييزية ، استخدم .options
مع التدمير.
const A = z . discriminatedUnion ( "status" , [
/* options */
] ) ;
const B = z . discriminatedUnion ( "status" , [
/* options */
] ) ;
const AB = z . discriminatedUnion ( "status" , [ ... A . options , ... B . options ] ) ;
يتم استخدام مخططات السجلات للتحقق من صحة الأنواع مثل Record<string, number>
. هذا مفيد بشكل خاص لتخزين أو تخزين العناصر التخزين المؤقت عن طريق المعرف.
const User = z . object ( { name : z . string ( ) } ) ;
const UserStore = z . record ( z . string ( ) , User ) ;
type UserStore = z . infer < typeof UserStore > ;
// => Record<string, { name: string }>
يمكن استخدام المخطط والنوع المستنتج مثل:
const userStore : UserStore = { } ;
userStore [ "77d2586b-9e8e-4ecf-8b21-ea7e0530eadd" ] = {
name : "Carlotta" ,
} ; // passes
userStore [ "77d2586b-9e8e-4ecf-8b21-ea7e0530eadd" ] = {
whatever : "Ice cream sundae" ,
} ; // TypeError
ملاحظة على المفاتيح العددية
على الرغم من أن z.record(keyType, valueType)
قادر على قبول أنواع المفاتيح العددية وأن نوع السجل المدمج لـ TypeScript هو Record<KeyType, ValueType>
، من الصعب تمثيل Record<number, any>
في Zod.
كما اتضح ، فإن سلوك TypeScript المحيط [k: number]
غير بديهي بعض الشيء:
const testMap : { [ k : number ] : string } = {
1 : "one" ,
} ;
for ( const key in testMap ) {
console . log ( ` ${ key } : ${ typeof key } ` ) ;
}
// prints: `1: string`
كما ترون ، تقوم JavaScript تلقائيًا بإلقاء جميع مفاتيح الكائنات على الأوتار أسفل الغطاء. نظرًا لأن Zod يحاول سد الفجوة بين أنواع وقت التشغيل الثابتة ووقت التشغيل ، فليس من المنطقي توفير طريقة لإنشاء مخطط تسجيل مع مفاتيح رقمية ، نظرًا لعدم وجود شيء مثل المفتاح العددي في وقت التشغيل JavaScript.
const stringNumberMap = z . map ( z . string ( ) , z . number ( ) ) ;
type StringNumberMap = z . infer < typeof stringNumberMap > ;
// type StringNumberMap = Map<string, number>
const numberSet = z . set ( z . number ( ) ) ;
type NumberSet = z . infer < typeof numberSet > ;
// type NumberSet = Set<number>
يمكن أن تكون Set Schemas أكثر تقييدًا مع طرق الأداة المساعدة التالية.
z . set ( z . string ( ) ) . nonempty ( ) ; // must contain at least one item
z . set ( z . string ( ) ) . min ( 5 ) ; // must contain 5 or more items
z . set ( z . string ( ) ) . max ( 5 ) ; // must contain 5 or fewer items
z . set ( z . string ( ) ) . size ( 5 ) ; // must contain 5 items exactly
التقاطعات مفيدة لإنشاء أنواع "منطقية و". هذا مفيد لتبادل نوعين من الكائنات.
const Person = z . object ( {
name : z . string ( ) ,
} ) ;
const Employee = z . object ( {
role : z . string ( ) ,
} ) ;
const EmployedPerson = z . intersection ( Person , Employee ) ;
// equivalent to:
const EmployedPerson = Person . and ( Employee ) ;
على الرغم من أنه في كثير من الحالات ، يوصى باستخدام A.merge(B)
لدمج كائنين. تقوم طريقة .merge
بإرجاع مثيل ZodObject
جديد ، في حين أن A.and(B)
إرجاع مثيل ZodIntersection
الأقل فائدة الذي يفتقر إلى طرق الكائن المشتركة مثل pick
omit
.
const a = z . union ( [ z . number ( ) , z . string ( ) ] ) ;
const b = z . union ( [ z . number ( ) , z . boolean ( ) ] ) ;
const c = z . intersection ( a , b ) ;
type c = z . infer < typeof c > ; // => number
يمكنك تحديد مخطط متكرر في Zod ، ولكن بسبب قيود على TypeScript ، لا يمكن استنتاج نوعها بشكل ثابت. بدلاً من ذلك ، ستحتاج إلى تحديد تعريف النوع يدويًا ، وتوفيره إلى Zod كـ "تلميح نوع".
const baseCategorySchema = z . object ( {
name : z . string ( ) ,
} ) ;
type Category = z . infer < typeof baseCategorySchema > & {
subcategories : Category [ ] ;
} ;
const categorySchema : z . ZodType < Category > = baseCategorySchema . extend ( {
subcategories : z . lazy ( ( ) => categorySchema . array ( ) ) ,
} ) ;
categorySchema . parse ( {
name : "People" ,
subcategories : [
{
name : "Politicians" ,
subcategories : [
{
name : "Presidents" ,
subcategories : [ ] ,
} ,
] ,
} ,
] ,
} ) ; // passes
بفضل كراسيت لهذا المثال.
عند استخدام z.ZodType
مع z.ZodEffects
( .refine
، .transform
، preprocess
، إلخ ...) ، ستحتاج إلى تحديد أنواع المدخلات والمخرجات من المخطط. z.ZodType<Output, z.ZodTypeDef, Input>
const isValidId = ( id : string ) : id is `${ string } /${ string } ` =>
id . split ( "/" ) . length === 2 ;
const baseSchema = z . object ( {
id : z . string ( ) . refine ( isValidId ) ,
} ) ;
type Input = z . input < typeof baseSchema > & {
children : Input [ ] ;
} ;
type Output = z . output < typeof baseSchema > & {
children : Output [ ] ;
} ;
const schema : z . ZodType < Output , z . ZodTypeDef , Input > = baseSchema . extend ( {
children : z . lazy ( ( ) => schema . array ( ) ) ,
} ) ;
بفضل Marcus13371337 و Joelbeeldi لهذا المثال.
إذا كنت ترغب في التحقق من صحة أي قيمة JSON ، فيمكنك استخدام المقتطف أدناه.
const literalSchema = z . union ( [ z . string ( ) , z . number ( ) , z . boolean ( ) , z . null ( ) ] ) ;
type Literal = z . infer < typeof literalSchema > ;
type Json = Literal | { [ key : string ] : Json } | Json [ ] ;
const jsonSchema : z . ZodType < Json > = z . lazy ( ( ) =>
z . union ( [ literalSchema , z . array ( jsonSchema ) , z . record ( jsonSchema ) ] )
) ;
jsonSchema . parse ( data ) ;
بفضل Ggoodman على اقتراح هذا.
على الرغم من دعم المخططات العودية ، فإن تمرير البيانات الدورية إلى ZOD سيؤدي إلى حلقة لا حصر لها في بعض الحالات.
للكشف عن الأشياء الدورية قبل أن تسبب مشاكل ، فكر في هذا النهج.
const numberPromise = z . promise ( z . number ( ) ) ;
"التحليل" يعمل بشكل مختلف قليلاً مع مخططات الوعد. يحدث التحقق من الصحة في جزأين:
.catch
أن الإدخال هو مثيل للوعد (أي كائن مع .then
..then
يرفق خطوة التحقق من الصحة الإضافية على الوعد الحالي. You'll have to use .catch
on the returned Promise to handle validation failures. numberPromise . parse ( "tuna" ) ;
// ZodError: Non-Promise type: string
numberPromise . parse ( Promise . resolve ( "tuna" ) ) ;
// => Promise<number>
const test = async ( ) => {
await numberPromise . parse ( Promise . resolve ( "tuna" ) ) ;
// ZodError: Non-number type: string
await numberPromise . parse ( Promise . resolve ( 3.14 ) ) ;
// => 3.14
} ;
يمكنك استخدام z.instanceof
للتحقق من أن الإدخال هو مثيل للفئة. هذا مفيد للتحقق من صحة المدخلات مقابل الفئات التي يتم تصديرها من مكتبات الطرف الثالث.
class Test {
name : string ;
}
const TestSchema = z . instanceof ( Test ) ;
const blob : any = "whatever" ;
TestSchema . parse ( new Test ( ) ) ; // passes
TestSchema . parse ( blob ) ; // throws
يتيح لك Zod أيضًا تحديد "مخططات الوظائف". هذا يجعل من السهل التحقق من صحة مدخلات ومخرجات دالة دون استقلال رمز التحقق من الصحة و "منطق العمل".
يمكنك إنشاء مخطط دالة مع z.function(args, returnType)
.
const myFunction = z . function ( ) ;
type myFunction = z . infer < typeof myFunction > ;
// => ()=>unknown
تحديد المدخلات والمخرجات.
const myFunction = z
. function ( )
. args ( z . string ( ) , z . number ( ) ) // accepts an arbitrary number of arguments
. returns ( z . boolean ( ) ) ;
type myFunction = z . infer < typeof myFunction > ;
// => (arg0: string, arg1: number)=>boolean
تحتوي مخططات الوظائف على طريقة .implement()
التي تقبل وظيفة وإرجاع وظيفة جديدة تقوم تلقائيًا بالتحقق من مدخلاتها ومخرجاتها.
const trimmedLength = z
. function ( )
. args ( z . string ( ) ) // accepts an arbitrary number of arguments
. returns ( z . number ( ) )
. implement ( ( x ) => {
// TypeScript knows x is a string!
return x . trim ( ) . length ;
} ) ;
trimmedLength ( "sandwich" ) ; // => 8
trimmedLength ( " asdf " ) ; // => 4
إذا كنت تهتم فقط بالتحقق من صحة المدخلات ، فلا تتصل بالطريقة .returns()
. سيتم استنتاج نوع الإخراج من التنفيذ.
يمكنك استخدام خيار
z.void()
الخاص إذا لم ترجع وظيفتك أي شيء. سيتيح هذا Zod استنتاج نوع وظائف إعادة تشغيل الفراغ بشكل صحيح. (وظائف إعادة إرجاع الفراغ تعود بالفعل غير محددة.)
const myFunction = z
. function ( )
. args ( z . string ( ) )
. implement ( ( arg ) => {
return [ arg . length ] ;
} ) ;
myFunction ; // (arg: string)=>number[]
استخراج مخططات الإدخال والإخراج من مخطط الوظيفة.
myFunction . parameters ( ) ;
// => ZodTuple<[ZodString, ZodNumber]>
myFunction . returnType ( ) ;
// => ZodBoolean
يدعم Zod الآن الإكراه البدائي دون الحاجة إلى
.preprocess()
انظر مستندات الإكراه لمزيد من المعلومات.
عادةً ما يعمل Zod تحت نموذج "تحليل ثم تحويل". يقوم ZOD بالتحقق من الإدخال أولاً ، ثم يمررها من خلال سلسلة من وظائف التحول. (لمزيد من المعلومات حول التحويلات ، اقرأ مستندات .transform.)
لكن في بعض الأحيان تريد تطبيق بعض التحويل على المدخلات قبل حدوث تحليل. حالة الاستخدام الشائعة: اكتب الإكراه. ZOD يتيح هذا مع z.preprocess()
.
const castToString = z . preprocess ( ( val ) => String ( val ) , z . string ( ) ) ;
هذا يعيد مثيل ZodEffects
. ZodEffects
عبارة عن فئة غلاف تحتوي على جميع المنطق المتعلق بالمعالجة المسبقة والتحسينات والتحويلات.
يمكنك إنشاء مخطط ZOD لأي نوع من أنواع TypeScript باستخدام z.custom()
. يعد هذا مفيدًا لإنشاء مخططات للأنواع التي لا تدعمها Zod Out Out of the Box ، مثل حرفية سلسلة القوالب.
const px = z . custom < `${ number } px` > ( ( val ) => {
return typeof val === "string" ? / ^d+px$ / . test ( val ) : false ;
} ) ;
type px = z . infer < typeof px > ; // `${number}px`
px . parse ( "42px" ) ; // "42px"
px . parse ( "42vw" ) ; // throws;
إذا لم تقدم وظيفة التحقق من الصحة ، فسوف يسمح ZOD بأي قيمة. هذا يمكن أن يكون خطير!
z . custom < { arg : string } > ( ) ; // performs no validation
يمكنك تخصيص رسالة الخطأ والخيارات الأخرى عن طريق تمرير وسيطة ثانية. تعمل هذه المعلمة بنفس طريقة معلمة Params لـ .refine
.
z . custom < ... > ( ( val ) => ... , "custom error message" ) ;
جميع مخططات zod تحتوي على طرق معينة.
.parse
.parse(data: unknown): T
بالنظر إلى أي مخطط ZOD ، يمكنك استدعاء طريقة .parse
للتحقق من data
صالحة. إذا كان الأمر كذلك ، يتم إرجاع القيمة بمعلومات النوع الكامل! خلاف ذلك ، يتم طرح خطأ.
هام: القيمة التي تم إرجاعها بواسطة
.parse
هي استنساخ عميق للمتغير الذي مررت به.
const stringSchema = z . string ( ) ;
stringSchema . parse ( "fish" ) ; // => returns "fish"
stringSchema . parse ( 12 ) ; // throws error
.parseAsync
.parseAsync(data:unknown): Promise<T>
إذا كنت تستخدم التحسينات غير المتزامنة أو التحويلات (المزيد على تلك لاحقًا) ، فستحتاج إلى استخدام .parseAsync
.
const stringSchema = z . string ( ) . refine ( async ( val ) => val . length <= 8 ) ;
await stringSchema . parseAsync ( "hello" ) ; // => returns "hello"
await stringSchema . parseAsync ( "hello world" ) ; // => throws error
.safeParse
.safeParse(data:unknown): { success: true; data: T; } | { success: false; error: ZodError; }
إذا كنت لا تريد أن يرمي Zod الأخطاء عند فشل التحقق ، فاستخدم .safeParse
. تقوم هذه الطريقة بإرجاع كائن يحتوي على البيانات المحسورة بنجاح أو مثيل Zoderror الذي يحتوي على معلومات مفصلة حول مشاكل التحقق.
stringSchema . safeParse ( 12 ) ;
// => { success: false; error: ZodError }
stringSchema . safeParse ( "billie" ) ;
// => { success: true; data: 'billie' }
والنتيجة هي اتحاد تمييز ، حتى تتمكن من التعامل مع الأخطاء بشكل مريح للغاية:
const result = stringSchema . safeParse ( "billie" ) ;
if ( ! result . success ) {
// handle error then return
result . error ;
} else {
// do something
result . data ;
}
.safeParseAsync
Alias:
.spa
نسخة غير متزامنة من safeParse
.
await stringSchema . safeParseAsync ( "billie" ) ;
للراحة ، كان هذا مستعزًا لـ .spa
:
await stringSchema . spa ( "billie" ) ;
.refine
.refine(validator: (data:T)=>any, params?: RefineParams)
يتيح لك Zod تقديم منطق التحقق من الصحة المخصص عبر التحسينات . (للاطلاع على الميزات المتقدمة مثل إنشاء مشكلات متعددة وتخصيص رموز الخطأ ، انظر .superRefine
.)
تم تصميم Zod لتعكس TypeScript بأكبر قدر ممكن. ولكن هناك العديد من "أنواع التحسينات" المزعومة التي قد ترغب في التحقق منها لا يمكن تمثيلها في نظام نوع TypeScript. على سبيل المثال: التحقق من أن الرقم هو عدد صحيح أو أن السلسلة هي عنوان بريد إلكتروني صالح.
على سبيل المثال ، يمكنك تحديد التحقق من الصحة المخصصة على أي مخطط zod مع .refine
:
const myString = z . string ( ) . refine ( ( val ) => val . length <= 255 , {
message : "String can't be more than 255 characters" ,
} ) ;
️ وظائف التحسين يجب ألا ترمي. بدلاً من ذلك ، يجب عليهم إرجاع قيمة falsy إلى فشل الإشارة.
كما ترون ، يأخذ .refine
حجتين.
T
- النوع المستنتج من المخطط) ويعيد any
. أي قيمة الحقيقة سوف تمر التحقق من الصحة. (قبل [email protected] كان على وظيفة التحقق من الصحة إرجاع منطقية.) type RefineParams = {
// override error message
message ?: string ;
// appended to error path
path ?: ( string | number ) [ ] ;
// params object you can use to customize message
// in error map
params ?: object ;
} ;
بالنسبة للحالات المتقدمة ، يمكن أن تكون الوسيطة الثانية أيضًا وظيفة تعيد RefineParams
.
const longString = z . string ( ) . refine (
( val ) => val . length > 10 ,
( val ) => ( { message : ` ${ val } is not more than 10 characters` } )
) ;
const passwordForm = z
. object ( {
password : z . string ( ) ,
confirm : z . string ( ) ,
} )
. refine ( ( data ) => data . password === data . confirm , {
message : "Passwords don't match" ,
path : [ "confirm" ] , // path of error
} ) ;
passwordForm . parse ( { password : "asdf" , confirm : "qwer" } ) ;
نظرًا لأنك قدمت معلمة path
، سيكون الخطأ الناتج هو:
ZodError {
issues : [ {
"code" : "custom" ,
"path" : [ "confirm" ] ,
"message" : "Passwords don't match"
} ]
}
يمكن أن تكون التحسينات أيضًا غير متزامنة:
const userId = z . string ( ) . refine ( async ( id ) => {
// verify that ID exists in database
return true ;
} ) ;
️ إذا كنت تستخدم تحسينات ASYNC ، فيجب عليك استخدام طريقة.parseAsync
لتحليل البيانات! وإلا فإن Zod سوف يرمي خطأ.
التحويلات والتحسينات يمكن أن تكون متشابكة:
z . string ( )
. transform ( ( val ) => val . length )
. refine ( ( val ) => val > 25 ) ;
.superRefine
طريقة .refine
هي في الواقع السكر النحوي فوق طريقة أكثر تنوعا (والامنح) تسمى superRefine
. هذا مثال:
const Strings = z . array ( z . string ( ) ) . superRefine ( ( val , ctx ) => {
if ( val . length > 3 ) {
ctx . addIssue ( {
code : z . ZodIssueCode . too_big ,
maximum : 3 ,
type : "array" ,
inclusive : true ,
message : "Too many items ?" ,
} ) ;
}
if ( val . length !== new Set ( val ) . size ) {
ctx . addIssue ( {
code : z . ZodIssueCode . custom ,
message : `No duplicates allowed.` ,
} ) ;
}
} ) ;
يمكنك إضافة العديد من المشكلات كما تريد. إذا لم يتم استدعاء ctx.addIssue
أثناء تنفيذ الوظيفة ، فإن التحقق من الصحة يمر.
عادةً ما تنشئ التحسينات دائمًا مشكلات مع رمز الخطأ ZodIssueCode.custom
، ولكن مع superRefine
من الممكن إلقاء مشكلات في أي ZodIssueCode
. يتم وصف كل رمز مشكلة بالتفصيل في دليل معالجة الأخطاء: error_handling.md.
بشكل افتراضي ، سيستمر التحليل حتى بعد فشل فحص الصقل. على سبيل المثال ، إذا قمت بسلسلة تحسينات متعددة ، فسيتم تنفيذها جميعًا. ومع ذلك ، قد يكون من المرغوب فيه الإجهاض مبكرًا لمنع تنفيذ التحسينات اللاحقة. لتحقيق ذلك ، تمرير العلم fatal
إلى ctx.addIssue
وإرجاع z.NEVER
.
const schema = z . number ( ) . superRefine ( ( val , ctx ) => {
if ( val < 10 ) {
ctx . addIssue ( {
code : z . ZodIssueCode . custom ,
message : "should be >= 10" ,
fatal : true ,
} ) ;
return z . NEVER ;
}
if ( val !== 12 ) {
ctx . addIssue ( {
code : z . ZodIssueCode . custom ,
message : "should be twelve" ,
} ) ;
}
} ) ;
إذا قمت بتقديم نوع مسند لـ .refine()
أو .superRefine()
، فسيتم تضييق النوع الناتج إلى نوع المسند الخاص بك. هذا مفيد إذا كنت تمزج تحسينات وتحولات متعددة بالسلاسل:
const schema = z
. object ( {
first : z . string ( ) ,
second : z . number ( ) ,
} )
. nullable ( )
. superRefine ( ( arg , ctx ) : arg is { first : string ; second : number } => {
if ( ! arg ) {
ctx . addIssue ( {
code : z . ZodIssueCode . custom , // customize your issue
message : "object should exist" ,
} ) ;
}
return z . NEVER ; // The return value is not used, but we need to return something to satisfy the typing
} )
// here, TS knows that arg is not null
. refine ( ( arg ) => arg . first === "bob" , "`first` is not `bob`!" ) ;
️ يجب عليك استخدامctx.addIssue()
بدلاً من إرجاع قيمة منطقية للإشارة إلى ما إذا كان التحقق من الصحة. Ifctx.addIssue
is not called during the execution of the function, validation passes.
.transform
لتحويل البيانات بعد التحليل ، استخدم طريقة transform
.
const stringToNumber = z . string ( ) . transform ( ( val ) => val . length ) ;
stringToNumber . parse ( "string" ) ; // => 6
لاحظ أن stringToNumber
أعلاه هو مثيل للفئة الفرعية ZodEffects
. انها ليست مثال على ZodString
. إذا كنت ترغب في استخدام الأساليب المدمجة لـ ZodString
(على سبيل المثال .email()
) ، فيجب عليك تطبيق هذه الطرق قبل أي تحويلات.
const emailToDomain = z
. string ( )
. email ( )
. transform ( ( val ) => val . split ( "@" ) [ 1 ] ) ;
emailToDomain . parse ( "[email protected]" ) ; // => example.com
يمكن أن تتحقق طريقة .transform
في وقت واحد من صحة وتحويل القيمة. غالبًا ما يكون هذا أبسط وأقل نسبة من transform
للسلاسل refine
.
كما هو الحال مع .superRefine
، تستقبل وظيفة التحويل كائن ctx
مع طريقة addIssue
التي يمكن استخدامها لتسجيل مشكلات التحقق من الصحة.
const numberInString = z . string ( ) . transform ( ( val , ctx ) => {
const parsed = parseInt ( val ) ;
if ( isNaN ( parsed ) ) {
ctx . addIssue ( {
code : z . ZodIssueCode . custom ,
message : "Not a number" ,
} ) ;
// This is a special symbol you can use to
// return early from the transform function.
// It has type `never` so it does not affect the
// inferred return type.
return z . NEVER ;
}
return parsed ;
} ) ;
التحويلات والتحسينات يمكن أن تكون متشابكة. سيتم تنفيذها بالترتيب الذي تم الإعلان عنه.
const nameToGreeting = z
. string ( )
. transform ( ( val ) => val . toUpperCase ( ) )
. refine ( ( val ) => val . length > 15 )
. transform ( ( val ) => `Hello ${ val } ` )
. refine ( ( val ) => val . indexOf ( "!" ) === - 1 ) ;
يمكن أن تكون التحويلات أيضًا غير متزامنة.
const IdToUser = z
. string ( )
. uuid ( )
. transform ( async ( id ) => {
return await getUserById ( id ) ;
} ) ;
️ إذا كان مخططك يحتوي على تحويلات غير متزامنة ، فيجب عليك استخدام .Parseasync () أو .safeparseasync () لتحليل البيانات. وإلا فإن Zod سوف يرمي خطأ.
.default
You can use transforms to implement the concept of "default values" in Zod.
const stringWithDefault = z . string ( ) . default ( "tuna" ) ;
stringWithDefault . parse ( undefined ) ; // => "tuna"
اختياريا ، يمكنك تمرير وظيفة إلى .default
سيتم إعادة تنفيذها كلما تحتاج القيمة الافتراضية إلى إنشاء:
const numberWithRandomDefault = z . number ( ) . default ( Math . random ) ;
numberWithRandomDefault . parse ( undefined ) ; // => 0.4413456736055323
numberWithRandomDefault . parse ( undefined ) ; // => 0.1871840107401901
numberWithRandomDefault . parse ( undefined ) ; // => 0.7223408162401552
من الناحية المفاهيمية ، هذه هي الطريقة التي تعالج ZOD القيم الافتراضية:
undefined
الإدخال ، يتم إرجاع القيمة الافتراضية.describe
استخدم .describe()
لإضافة خاصية description
إلى المخطط الناتج.
const documentedString = z
. string ( )
. describe ( "A useful bit of text, if you know what to do with it." ) ;
documentedString . description ; // A useful bit of text…
يمكن أن يكون هذا مفيدًا لتوثيق حقل ، على سبيل المثال في مخطط JSON باستخدام مكتبة مثل zod-to-json-schema
).
.catch
استخدم .catch()
لتوفير "قيمة الصيد" المراد إرجاعها في حالة وجود خطأ في التحليل.
const numberWithCatch = z . number ( ) . catch ( 42 ) ;
numberWithCatch . parse ( 5 ) ; // => 5
numberWithCatch . parse ( "tuna" ) ; // => 42
اختياريا ، يمكنك تمرير وظيفة إلى .catch
التي سيتم إعادة تنفيذها كلما تحتاج القيمة الافتراضية. سيتم تمرير كائن ctx
الذي يحتوي على خطأ تم التقاطه في هذه الوظيفة.
const numberWithRandomCatch = z . number ( ) . catch ( ( ctx ) => {
ctx . error ; // the caught ZodError
return Math . random ( ) ;
} ) ;
numberWithRandomCatch . parse ( "sup" ) ; // => 0.4413456736055323
numberWithRandomCatch . parse ( "sup" ) ; // => 0.1871840107401901
numberWithRandomCatch . parse ( "sup" ) ; // => 0.7223408162401552
من الناحية المفاهيمية ، هذه هي الطريقة التي تعالج ZOD "قيم الصيد":
.optional
طريقة الراحة التي تُرجع نسخة اختيارية من المخطط.
const optionalString = z . string ( ) . optional ( ) ; // string | undefined
// equivalent to
z . optional ( z . string ( ) ) ;
.nullable
طريقة الراحة التي تُرجع نسخة لاغية من المخطط.
const nullableString = z . string ( ) . nullable ( ) ; // string | null
// equivalent to
z . nullable ( z . string ( ) ) ;
.nullish
طريقة الراحة التي تُرجع نسخة "nullish" من المخطط. سوف تقبل مخططات Nullish كل من undefined
و null
. اقرأ المزيد عن مفهوم "Nullish" في ملاحظات الإصدار TypeScript 3.7.
const nullishString = z . string ( ) . nullish ( ) ; // string | null | undefined
// equivalent to
z . string ( ) . nullable ( ) . optional ( ) ;
.array
طريقة الراحة التي تُرجع مخطط صفيف للنوع المعطى:
const stringArray = z . string ( ) . array ( ) ; // string[]
// equivalent to
z . array ( z . string ( ) ) ;
.promise
طريقة الراحة لأنواع الوعد:
const stringPromise = z . string ( ) . promise ( ) ; // Promise<string>
// equivalent to
z . promise ( z . string ( ) ) ;
.or
طريقة الراحة لأنواع النقابات.
const stringOrNumber = z . string ( ) . or ( z . number ( ) ) ; // string | number
// equivalent to
z . union ( [ z . string ( ) , z . number ( ) ] ) ;
.and
طريقة الراحة لإنشاء أنواع التقاطع.
const nameAndAge = z
. object ( { name : z . string ( ) } )
. and ( z . object ( { age : z . number ( ) } ) ) ; // { name: string } & { age: number }
// equivalent to
z . intersection ( z . object ( { name : z . string ( ) } ) , z . object ( { age : z . number ( ) } ) ) ;
.brand
.brand<T>() => ZodBranded<this, B>
يعد نظام نوع TypeScript هيكليًا ، مما يعني أن أي نوعين من المكافئين هيكليًا يعتبران متماثلين.
type Cat = { name : string } ;
type Dog = { name : string } ;
const petCat = ( cat : Cat ) => { } ;
const fido : Dog = { name : "fido" } ;
petCat ( fido ) ; // works fine
في بعض الحالات ، يمكن أن يكون من المرغوب فيه محاكاة الكتابة الاسمية داخل TypeScript. For instance, you may wish to write a function that only accepts an input that has been validated by Zod. يمكن تحقيق ذلك مع الأنواع ذات العلامات التجارية (ويعرف أيضًا باسم أنواع غير شفافة ).
const Cat = z . object ( { name : z . string ( ) } ) . brand < "Cat" > ( ) ;
type Cat = z . infer < typeof Cat > ;
const petCat = ( cat : Cat ) => { } ;
// this works
const simba = Cat . parse ( { name : "simba" } ) ;
petCat ( simba ) ;
// this doesn't
petCat ( { name : "fido" } ) ;
تحت الغطاء ، يعمل هذا من خلال ربط "العلامة التجارية" بالنوع المستنتج باستخدام نوع التقاطع. وبهذه الطريقة ، لم تعد هياكل البيانات العادية/دون علامات تجارية قابلة للتخصيص للنوع المستنتج من المخطط.
const Cat = z . object ( { name : z . string ( ) } ) . brand < "Cat" > ( ) ;
type Cat = z . infer < typeof Cat > ;
// {name: string} & {[symbol]: "Cat"}
لاحظ أن الأنواع ذات العلامات التجارية لا تؤثر على نتيجة وقت التشغيل لـ .parse
. إنه بناء ثابت فقط.
.readonly
.readonly() => ZodReadonly<this>
تقوم هذه الطريقة بإرجاع مثيل مخطط ZodReadonly
الذي يوسع المدخلات باستخدام مخطط الأساس ، ثم يستدعي Object.freeze()
على النتيجة. كما تم وضع علامة على النوع المستنتج على أنه readonly
.
const schema = z . object ( { name : z . string ( ) } ) . readonly ( ) ;
type schema = z . infer < typeof schema > ;
// Readonly<{name: string}>
const result = schema . parse ( { name : "fido" } ) ;
result . name = "simba" ; // error
يستخدم النوع المستنتج الأنواع المدمجة في TypeScript عندما تكون ذات صلة.
z . array ( z . string ( ) ) . readonly ( ) ;
// readonly string[]
z . tuple ( [ z . string ( ) , z . number ( ) ] ) . readonly ( ) ;
// readonly [string, number]
z . map ( z . string ( ) , z . date ( ) ) . readonly ( ) ;
// ReadonlyMap<string, Date>
z . set ( z . string ( ) ) . readonly ( ) ;
// ReadonlySet<string>
.pipe
يمكن ربط المخططات في التحقق من الصحة "خطوط أنابيب". من المفيد التحقق من صحة النتيجة بسهولة بعد نقل .transform()
:
z . string ( )
. transform ( ( val ) => val . length )
. pipe ( z . number ( ) . min ( 5 ) ) ;
طريقة .pipe()
تُرجع مثيل ZodPipeline
.
.pipe()
لإصلاح المشكلات الشائعة مع z.coerce
. يمكنك تقييد المدخلات على الأنواع التي تعمل بشكل جيد مع الإكراه الذي اخترته. ثم استخدم .pipe()
لتطبيق الإكراه.
بدون مدخلات مقيدة:
const toDate = z . coerce . date ( ) ;
// works intuitively
console . log ( toDate . safeParse ( "2023-01-01" ) . success ) ; // true
// might not be what you want
console . log ( toDate . safeParse ( null ) . success ) ; // true
مع المدخلات المقيدة:
const datelike = z . union ( [ z . number ( ) , z . string ( ) , z . date ( ) ] ) ;
const datelikeToDate = datelike . pipe ( z . coerce . date ( ) ) ;
// still works intuitively
console . log ( datelikeToDate . safeParse ( "2023-01-01" ) . success ) ; // true
// more likely what you want
console . log ( datelikeToDate . safeParse ( null ) . success ) ; // false
يمكنك أيضًا استخدام هذه التقنية لتجنب الإكراه الذي يرمي أخطاء غير معلنة.
without constrained input:
const toBigInt = z . coerce . bigint ( ) ;
// works intuitively
console . log ( toBigInt . safeParse ( "42" ) ) ; // true
// probably not what you want
console . log ( toBigInt . safeParse ( null ) ) ; // throws uncaught error
مع المدخلات المقيدة:
const toNumber = z . number ( ) . or ( z . string ( ) ) . pipe ( z . coerce . number ( ) ) ;
const toBigInt = z . bigint ( ) . or ( toNumber ) . pipe ( z . coerce . bigint ( ) ) ;
// still works intuitively
console . log ( toBigInt . safeParse ( "42" ) . success ) ; // true
// error handled by zod, more likely what you want
console . log ( toBigInt . safeParse ( null ) . success ) ; // false
يمكنك استخراج نوع TypeScript من أي مخطط مع z.infer<typeof mySchema>
.
const A = z . string ( ) ;
type A = z . infer < typeof A > ; // string
const u : A = 12 ; // TypeError
const u : A = "asdf" ; // compiles
ماذا عن التحويلات؟
في الواقع ، يتعقب كل مخطط ZOD داخليًا نوعين : المدخلات والإخراج. بالنسبة لمعظم المخططات (على سبيل المثال z.string()
) هذان هما متماثلان. ولكن بمجرد إضافة التحويلات إلى المزيج ، يمكن أن تتباعد هاتان القيمتان. على سبيل المثال z.string().transform(val => val.length)
لديه إدخال string
وإخراج number
.
يمكنك استخراج أنواع الإدخال والإخراج بشكل منفصل مثل:
const stringToNumber = z . string ( ) . transform ( ( val ) => val . length ) ;
// ️ Important: z.infer returns the OUTPUT type!
type input = z . input < typeof stringToNumber > ; // string
type output = z . output < typeof stringToNumber > ; // number
// equivalent to z.output!
type inferred = z . infer < typeof stringToNumber > ; // number
باستخدام TypeScript Generics ، يمكنك كتابة وظائف قابلة لإعادة الاستخدام التي تقبل مخططات ZOD كمعلمات. يمكّنك ذلك من إنشاء منطق التحقق من الصحة المخصص ، وتحولات المخطط ، وأكثر من ذلك ، مع الحفاظ على سلامة النوع واستدلاله.
عند محاولة كتابة وظيفة تقبل مخطط Zod كمدخل ، من المغري تجربة شيء مثل هذا:
function inferSchema < T > ( schema : z . ZodType < T > ) {
return schema ;
}
هذا النهج غير صحيح ، ويقصر قدرة TypeScript على استنتاج الحجة بشكل صحيح. بغض النظر عن ما تنقله ، سيكون نوع schema
مثالًا على ZodType
.
inferSchema ( z . string ( ) ) ;
// => ZodType<string>
This approach loses type information, namely which subclass the input actually is (in this case, ZodString
). That means you can't call any string-specific methods like .min()
on the result of inferSchema
.
A better approach is to infer the schema as a whole instead of merely its inferred type. You can do this with a utility type called z.ZodTypeAny
.
function inferSchema < T extends z . ZodTypeAny > ( schema : T ) {
return schema ;
}
inferSchema ( z . string ( ) ) ;
// => ZodString
ZodTypeAny
is just a shorthand forZodType<any, any, any>
, a type that is broad enough to match any Zod schema.
The Result is now fully and properly typed, and the type system can infer the specific subclass of the schema.
If you follow the best practice of using z.ZodTypeAny
as the generic parameter for your schema, you may encounter issues with the parsed data being typed as any
instead of the inferred type of the schema.
function parseData < T extends z . ZodTypeAny > ( data : unknown , schema : T ) {
return schema . parse ( data ) ;
}
parseData ( "sup" , z . string ( ) ) ;
// => any
Due to how TypeScript inference works, it is treating schema
like a ZodTypeAny
instead of the inferred type. You can fix this with a type cast using z.infer
.
function parseData < T extends z . ZodTypeAny > ( data : unknown , schema : T ) {
return schema . parse ( data ) as z . infer < T > ;
// ^^^^^^^^^^^^^^ <- add this
}
parseData ( "sup" , z . string ( ) ) ;
// => string
The ZodType
class has three generic parameters.
class ZodType <
Output = any ,
Def extends ZodTypeDef = ZodTypeDef ,
Input = Output
> { ... }
By constraining these in your generic input, you can limit what schemas are allowable as inputs to your function:
function makeSchemaOptional < T extends z . ZodType < string > > ( schema : T ) {
return schema . optional ( ) ;
}
makeSchemaOptional ( z . string ( ) ) ;
// works fine
makeSchemaOptional ( z . number ( ) ) ;
// Error: 'ZodNumber' is not assignable to parameter of type 'ZodType<string, ZodTypeDef, string>'
Zod provides a subclass of Error called ZodError
. ZodErrors contain an issues
array containing detailed information about the validation problems.
const result = z
. object ( {
name : z . string ( ) ,
} )
. safeParse ( { name : 12 } ) ;
if ( ! result . success ) {
result . error . issues ;
/* [
{
"code": "invalid_type",
"expected": "string",
"received": "number",
"path": [ "name" ],
"message": "Expected string, received number"
}
] */
}
For detailed information about the possible error codes and how to customize error messages, check out the dedicated error handling guide: ERROR_HANDLING.md
Zod's error reporting emphasizes completeness and correctness . If you are looking to present a useful error message to the end user, you should either override Zod's error messages using an error map (described in detail in the Error Handling guide) or use a third-party library like zod-validation-error
You can use the .format()
method to convert this error into a nested object.
const result = z
. object ( {
name : z . string ( ) ,
} )
. safeParse ( { name : 12 } ) ;
if ( ! result . success ) {
const formatted = result . error . format ( ) ;
/* {
name: { _errors: [ 'Expected string, received number' ] }
} */
formatted . name ?. _errors ;
// => ["Expected string, received number"]
}
There are a handful of other widely-used validation libraries, but all of them have certain design limitations that make for a non-ideal developer experience.
https://github.com/hapijs/joi
Doesn't support static type inference ?
https://github.com/jquense/yup
Yup is a full-featured library that was implemented first in vanilla JS, and later rewritten in TypeScript.
https://github.com/gcanti/io-ts
io-ts is an excellent library by gcanti. The API of io-ts heavily inspired the design of Zod.
In our experience, io-ts prioritizes functional programming purity over developer experience in many cases. This is a valid and admirable design goal, but it makes io-ts particularly hard to integrate into an existing codebase with a more procedural or object-oriented bias. For instance, consider how to define an object with optional properties in io-ts:
import * as t from "io-ts" ;
const A = t . type ( {
foo : t . string ,
} ) ;
const B = t . partial ( {
bar : t . number ,
} ) ;
const C = t . intersection ( [ A , B ] ) ;
type C = t . TypeOf < typeof C > ;
// returns { foo: string; bar?: number | undefined }
You must define the required and optional props in separate object validators, pass the optionals through t.partial
(which marks all properties as optional), then combine them with t.intersection
.
Consider the equivalent in Zod:
const C = z . object ( {
foo : z . string ( ) ,
bar : z . number ( ) . optional ( ) ,
} ) ;
type C = z . infer < typeof C > ;
// returns { foo: string; bar?: number | undefined }
This more declarative API makes schema definitions vastly more concise.
io-ts
also requires the use of gcanti's functional programming library fp-ts
to parse results and handle errors. This is another fantastic resource for developers looking to keep their codebase strictly functional. But depending on fp-ts
necessarily comes with a lot of intellectual overhead; a developer has to be familiar with functional programming concepts and the fp-ts
nomenclature to use the library.
fp-ts
compatibility[T, ...T[]]
)https://github.com/pelotom/runtypes
Good type inference support.
[T, ...T[]]
)https://github.com/sindresorhus/ow
Ow is focused on function input validation. It's a library that makes it easy to express complicated assert statements, but it doesn't let you parse untyped data. They support a much wider variety of types; Zod has a nearly one-to-one mapping with TypeScript's type system, whereas ow lets you validate several highly-specific types out of the box (eg int32Array
, see full list in their README).
If you want to validate function inputs, use function schemas in Zod! It's a much simpler approach that lets you reuse a function type declaration without repeating yourself (namely, copy-pasting a bunch of ow assertions at the beginning of every function). Also Zod lets you validate your return types as well, so you can be sure there won't be any unexpected data passed downstream.
View the changelog at CHANGELOG.md