JSON’dan Zod’a
JSON’dan Zod şemaları üretir, her biri z.infer türüyle: dizi öğeleri birleştirilir, isteğe bağlı ve nullable anahtarlar işaretlenir, hiçbir şey tahmin edilmez.
import * as z from "zod"
export const Customer = z.object({
name: z.string(),
email: z.string(),
phone: z.null(),
})
export type Customer = z.infer<typeof Customer>
export const LineItem = z.object({
sku: z.string(),
quantity: z.number(),
price: z.number(),
note: z.string().nullable(),
backordered: z.boolean().optional(),
})
export type LineItem = z.infer<typeof LineItem>
export const Root = z.object({
id: z.string(),
customer: Customer,
lineItems: z.array(LineItem),
tags: z.array(z.unknown()),
})
export type Root = z.infer<typeof Root>
Zod 4 sözdizimi; Zod 3’te de geçerlidir. z.object listelemediği her anahtarı atar, yani örneğinizde bulunmayan bir alan, ayrıştırdığı verilerden sessizce kaybolur.
Aşağıdaki her konumda örneğiniz yalnızca null içeriyordu, bu yüzden şema orada başka hiçbir şeyi kabul etmez.
Konumlar: 1
Customer.phone
Aşağıdaki her konumda her sayı tam sayıydı. z.number() 7.5 gibi değerleri de kabul eder, .int() ise tam sayı gerektirir; bu yüzden onu yalnızca bir değerin tam sayı olması gerektiğini bildiğiniz yerde kendiniz ekleyin.
Konumlar: 1
LineItem.quantity
Aşağıdaki her konumda hiçbir şey çıkarılamadı, bu yüzden şema orada bulunanı denetlemez: z.unknown() her değeri, z.record(z.string(), z.unknown()) ise her nesneyi kabul eder.
Konumlar: 1
Root.tags[]
Veri her geldiğinde çalışan bir denetim
Bir TypeScript türü kodunuz derlenirken denetlenir ve kod çalıştığında artık ortada yoktur. Bir Zod şeması, geride kalan parçadır: programınızın içinde çalışır ve her yükü geldiği anda — bir API yanıtı, bir webhook gövdesi, bir kuyruktan alınan bir mesaj — kodunuz ona güvenmeden önce denetler. O JSON’dan bir örnek yapıştırın; bu sayfa şemaları sizin için, bir modüle yapıştırılmaya hazır olarak yazar: anahtarları olan her nesne adlandırılmış bir "z.object" olur, bir dizinin öğeleri birleştirilip teke indirilir ve her şemanın ardından ürettiği TypeScript türü gelir.
Bu, JSON’dan TypeScript’e sayfasının aynı JSON için verdiği cevabın, türler yerine bir doğrulayıcı olarak yazılmış hâlidir. İki sayfa da tek bir çıkarımı okur; bu yüzden hangi anahtarların isteğe bağlı olduğu, hangi değerlerin union olduğu, null’ın eksik bir anahtardan nerede ayrı tutulduğu ve iç içe her nesnenin adının ne olduğu bir kez kararlaştırılır ve yalnızca iki kez yazılır. Bu birleştirme kuralları JSON’dan TypeScript’e sayfasındaki kılavuzun konusudur ve burada yeniden anlatılmaz. Bu kılavuz, şemanın çalıştığında gerçek veriyle ne yaptığını ve karar vermeyi size neyi bıraktığını anlatır.
Ne geçer, ne reddedilir
Bu sayfadan çıkan bir şema, kendisinden yazıldığı örneği Zod 3’te de Zod 4’te de aynı biçimde kabul eder; kendine ait bir bildirimi olan tek durum hariç: Zod 4’te sonsuz bir sayı. O örnekten öte, şemanın söylediği şeyin içinde kalan her şeyi kabul eder ve gerçekten alacağınız veride bu şöyle işler:
- Derinlik sınırına varmadan, ".optional()" olmadan yazılmış bir anahtar zorunludur. Onu dışarıda bırakan bir yük reddedilir; oraya şemanın adını anmadığı türde bir değer koyan bir yük de öyle — şemanın "z.number()" dediği yerde bir dize, "z.string()" dediği yerde bir nesne.
- ".optional()" ile yazılmış bir anahtar eksik olabilir, ".nullable()" ile yazılmış bir anahtar da null tutabilir. İkisi de başka hiçbir türde değeri geçirmez; bu yüzden var olan isteğe bağlı bir anahtar yine de şemanın adını andığı şeyi tutmak zorundadır.
- Bir "z.union" üyelerinden herhangi birini kabul eder, başka hiçbir şeyi değil. Bir "z.array", her biri öğeleri için yazılmış şemaya uyduğu sürece, hiç öğe içermemesi de dahil, herhangi sayıda öğe kabul eder.
- Örnekte hiçbir şey görünmeyen yerde — boş bir dizinin öğeleri, anahtarı olmayan bir nesne, derinlik sınırının ötesinde iç içe geçmiş bir değer — şema orada bulunanı denetlemez: her değeri ya da örnekteki nesnenin anahtarı olmadığı yerde her nesneyi kabul eder ve bir bildirim bunun nerede olduğunu söyler.
Şemanın listelemediği bir anahtar geçirilir ve sonra sonuçtan atılır; atma işte budur ve kendi bölümü vardır. Bir anahtar bütün bunların dışında kalır. "__proto__" diye yazılan bir anahtar köşeli parantez içinde, hesaplanan bir anahtar olarak yazılır, çünkü düz yazıldığında bir anahtarı adlandırmak yerine içinde bulunduğu nesnenin prototipini ayarlardı — yine de Zod’un iki sürümü de bir ayrıştırmanın geri verdiği sonuçta bu anahtarı döndürmez ve Zod 4 onun değerini hiç denetlemez.
Şema neden kendini asla sıkılaştırmaz
Her örnek, kanıtlayamadığı ortak özellikler taşır: her id bir tam sayı, her e-posta e-posta biçiminde, admin’den başka hiçbir şey demeyen bir rol. Bu bir düzenliliktir ve bir düzenlilik bir denetim değildir. Bir üretece yine de bir tanesini yazmak cazip gelir — id’lere ".int()", adreslere "z.email()", role bir değişmez — ve bu üreteç bunu hiçbir zaman yapmaz, çünkü bir örnek verilerinizin neyi tutabileceğini gösterir, neyi tutmak zorunda olduğunu asla göstermez. Verilerinizden daha sıkı bir tür size kendi makinenizde bir derleme hatasına mal olur. Verilerinizden daha sıkı bir şema üretim ortamında reddedilmiş bir isteğe mal olur: admin olmayan ilk rol, değeri 7.5 olan ilk id, desenin öngörmediği ilk adres.
Denetimler ayrıca ayaklarınızın altında kayardı. Zod 4’teki "z.uuid()", Zod 3’teki ".uuid()" yönteminin kabul ettiği UUID biçimli dizeleri reddeder, çünkü varyant bitlerini denetler; Zod 4’teki ".int()" de güvenli aralığın dışında olup Zod 3’tekinin geçirdiği bir tam sayıyı reddeder — bu yüzden bugünkü örnekten tahmin edilen bir denetim, kurduğunuz Zod’a göre farklı bir denetim olurdu. Demek ki örnekte yalnızca ima edilen hiçbir şey şemaya yazılmaz. Veri geldiğinde önem taşıdığı yerde sayfa bunun yerine size bir bildirimle söyler ve düzenleme size bırakılır.
Şemanın kendiliğinden söylemediği şeyler
Sayfa, şemanın altında, fark edip şemaya yazmadığı şeyleri listeler: aşağıdaki çeşitlerden geçerli olan her biri için, geçerli olduğu bütün konumlarla birlikte bir kez verilen bir bildirim. Bir konum, çıktının şeyleri adlandırdığı biçimde yazılır — bir anahtar için "Customer.phone", bir dizinin öğeleri için "Root.tags[]", tanımlayıcı olmadığı yerde ve "__proto__" için tırnak ve köşeli parantez içindeki anahtar — böylece şemada bir bakışta bulunabilir. Sayfayla birlikte yüklenen örnek, Infinity ile ilgili olan dışında her çeşidi gösterir.
- Yalnızca null. Örnek o konumda null’dan başka hiçbir şey içermedi, bu yüzden şema "z.null()" der ve ilk gerçek değeri reddeder. Alanın doldurulduğunda ne tutacağına karar verin ve bunu kendiniz yazın — söz gelimi "z.string().nullable()" — ya da içinde bir değer bulunan bir örnek yapıştırın.
- Infinity. 1e999 gibi, bir JavaScript sayısının tutabileceğinin ötesindeki bir sayı, JSON ayrıştırılırken Infinity ya da -Infinity olur. Zod 4’teki "z.number()" sonsuz bir sayıyı reddeder ve Zod 3’teki onu kabul eder; bu yüzden Zod 4’te bu, bir şemanın tam da kendisinden yapıldığı örneği reddettiği tek durumdur. Bunun doğurduğu soru şemayla değil veriyle ilgilidir: rakamlar herhangi bir doğrulayıcı onları görmeden kaybolur; bu yüzden o değerin hiç sayı olması gerekip gerekmediği sorusunu yanıtlamak, onu yazan şeye düşer.
- Tam sayılar. O konumdaki her sayı tam sayıydı ve "z.number()" 7.5 değerini de kabul eder. Bir değerin tam sayı olması gereken yerde — bir id, bir sayım, bir miktar — kendiniz ".int()" ekleyin; tesadüfen yuvarlak çıkmış bir fiyatsa ona dokunmayın. Bu bildirim, "Number.MIN_SAFE_INTEGER" ile "Number.MAX_SAFE_INTEGER" arasındaki güvenli aralığın dışında bir tam sayı tutan hiçbir konumda verilmez, çünkü Zod 4’teki ".int()" bunları reddeder ve bir şemaya kendi örneğini reddettirecek bir öneri, sayfanın vermeyeceği tek türdür.
- Çıkarılacak bir şey yok. Boş bir dizinin öğeleri, anahtarı olmayan bir nesne ve 100 düzeyden daha derine iç içe geçmiş bir değer çıkarıma dayanacak hiçbir şey vermez; bu yüzden şema orada bulunanı denetlemez: "z.unknown()" hangi değer olursa olsun her değeri, "z.record(z.string(), z.unknown())" de her nesneyi kabul eder. O dizinin öğeleri ve o nesnenin anahtarları olan bir örnek yapıştırın ya da şemanın o kısmını elle yazın.
Bir bildirim şemanın hiçbir baytını asla değiştirmez. Onun yanında, sayfanın dilinde bir cümledir ve işaret ettiği düzenlemeyi yapmak ya da atlamak size kalmıştır. Dize biçimleri, değişmezler ya da enum’lar hakkında bildirim yoktur: her biri, örnekte gösterilemeyecek bir kurala dair bir tahmin olurdu.
Şemanın listelemediği anahtarlar atılır
"z.object", listelemediği anahtarları taşıyan bir nesneyi geçirir ve bu anahtarları döndürdüğü sonucun dışında bırakır. Bu, iki sürümde de Zod’un varsayılanıdır ve bir şemanın yaptığı en sessiz şeydir: örneğinizde tesadüfen bulunmayan bir alan, bunu söyleyen hiçbir hata olmadan kodunuzun aldığı veriden kaybolur. Bu sayfadaki her şemanın altındaki cümlenin ondan söz etmesinin nedeni budur.
Sayfa sizin yerinize katı ile gevşek arasında seçim yapmaz, çünkü her biri bir örnekle gösterilebilecek olandan fazlasını iddia eder. Katı bir nesne listelemediği her anahtarı reddeder — bunlardan başka anahtar yok — ve hiçbir örnek bunu bir sonraki yük için kanıtlayamaz. Gevşek bir nesne fazladan anahtarları korur ve çıkarılan türü onlar için bir dizin imzası kazanır; bu yüzden TypeScript sayfasının cevabı olmaktan çıkardı. Atma, TypeScript türünün kabul ettiğini kabul edip yine de o türü çıkaran tek davranıştır — fazladan özellikleri olan bir değer de bir arayüzü karşılar. Başka türlü seçmek için şemayı elle değiştirin:
- Şemanın listelemediği anahtarları reddetmek için Zod 4’te çıktının "z.object" yazdığı yere "z.strictObject" yazın ya da Zod 3’te "z.object" çağrısına ".strict()" zincirleyin.
- Onları korumak için Zod 4’te "z.looseObject" yazın ya da Zod 3’te ".passthrough()" zincirleyin. Zod 4 bu iki Zod 3 yöntemini hâlâ çalıştırır ve onları eski diye adlandırır.
Çıktıdaki her nesne kendi başına bir şemadır, bu yüzden seçim nesne nesne yapılır: kökü katı yapmak, onun içinde iç içe duran nesnelerde hiçbir şeyi değiştirmez ve ısrar etme hakkınız yalnızca dış zarf üzerindeyken bu, sıklıkla istediğiniz şeydir.
Her şema ve türü için tek bir ad
Her şemanın ardından türü gelir — "export const Customer" satırının hemen ardından "export type Customer = z.infer<typeof Customer>" — ki zod.dev kendi örneklerini böyle yazar: veriyi denetleyen değer için ve onun ürettiği tür için tek bir ad, çünkü TypeScript değerleri ve türleri ayrı ad alanlarında tutar. O tür, JSON’dan TypeScript’e sayfasının aynı JSON için yazdırdığı türdür; ad ad ve anahtar anahtar, aynı isteğe bağlı anahtarlarla, aynı union’larla ve null aynı yerlerde — aşağıdaki istisnalar dışında.
Depo bu söze güvenmek yerine onu denetler: bir örnek derlemi önce iki sayfadan, sonra Zod’un her sürümüyle TypeScript derleyicisinden geçer ve derleyiciye her ad için iki türün özdeş olup olmadığı ve her birinin ötekine atanabilir olup olmadığı sorulur. Farklı yanıt verdiği yer, bir belgenin derinliklerindedir. Derinlik sınırının ötesinde, bir anahtarın "z.unknown()" tuttuğu yerde, Zod 3 o anahtarı isteğe bağlı olarak çıkarırken TypeScript sayfası onu zorunlu yapar. Ayrıca Zod 4’te derleyici, onlarca düzey derinliğe iç içe geçmiş bir dizinin türünden vazgeçer ve türün kendi satırında TS2589 hatasını verir; o satırın üstündeki şema hâlâ çalışır ve örneği hâlâ kabul eder ve yalnızca çıkarılan tür kaybolur.
İki karşılaştırma da isteğe bağlı bir anahtarı TypeScript’in varsayılan olarak okuduğu gibi okur. Bir proje açmadıkça kapalı olan "exactOptionalPropertyTypes" seçeneği altında, isteğe bağlı bir anahtarın "z.infer" türü, Zod’un hangi sürümünde olursa olsun, açık bir undefined değerini de kabul eder; TypeScript sayfasının türü ise kabul etmez.
Zod 4 için yazılmış ve Zod 3’te hâlâ geçerli
Çıktı, zod.dev’in kendi örneklerinin başladığı import satırının altında, iki ana sürümde de bulunanla sınırlı kalır — "z.object", "z.array", iki ya da daha çok üye üzerinde "z.union", "z.string()", "z.number()", "z.boolean()", "z.null()", "z.unknown()", "z.record(z.string(), z.unknown())", ".optional()", ".nullable()" ve "z.infer". İçindeki hiçbir şey Zod 4 ile gelmedi ve içindeki hiçbir şey orada kullanım dışı bırakılmamıştır; bu yüzden Zod 3’ten ayrılmamış bir proje onu olduğu gibi yapıştırabilir.
Yine de aynı metin ikisinde birebir aynı davranmaz ve her fark, bu kılavuzda önem taşıdığı yerde söylenir. Zod 4’teki "z.number()" sonsuz bir sayıyı reddeder, Zod 3’teki ise kabul eder; Infinity bildirimi budur. Değeri "z.unknown()" olan bir anahtar Zod 3’ün çıkarılan türünde isteğe bağlı, Zod 4’ünkinde zorunludur — Zod 4.4’ten itibaren veri ayrıştırılırken de zorunludur — ve bu çıktı böyle bir anahtarı derinlik sınırının ötesinden başka hiçbir yerde yazmaz. Zod 3, "__proto__" diye yazılan bir anahtarı denetler ve Zod 4 denetlemez. Ayrıca derleyici, onlarca düzey derinliğe iç içe geçmiş bir dizi için Zod 4’ün türünden vazgeçer; oysa Zod 3’ünkini hesaplar.
Çıktı olağan Zod için, yöntemlerle yazılmıştır: "z.string().nullable().optional()". Zod Mini aynı şemayı bunun yerine işlevlerle yazar, "z.optional(z.nullable(z.string()))"; bu yüzden Zod Mini çıktıyı olduğu hâliyle çalıştıramaz.
Kök neden en sona gelir
TypeScript sayfası kökü önce, kökün kullandığı nesneleri ondan sonra yazdırır, çünkü bir tür, onu tanımlayan satırdan önce kullanılabilir. Bir şema ise öyle kullanılamaz: o bir değerdir ve kendi tanımının üstünde okunan bir "const", modül hâlâ yüklenirken bir ReferenceError fırlatır. Bu yüzden buradaki her şema kullandığı her şemadan sonra gelir ve kök en sona gelir — örnekte önce Customer ile LineItem, sonra onları tutan Root — ve TypeScript sayfasını açan kökün bu sayfayı kapatmasının nedeni budur.
Böyle bir sıra her zaman vardır. Çıkarım bir ağaçtır, çıkardığı her nesne tam olarak tek bir yerden kullanılır; bu yüzden hiçbir şemanın kendisine ya da kendisinden sonra yazdırılan birine başvurması gerekmez ve çıktıya hiçbir zaman "z.lazy" gerekmez.
Sıkça sorulan sorular
- Bir id neden "z.number().int()" değil de "z.number()" oluyor?
- Çünkü bir örnek şimdiye kadarki her id’nin tam sayı olduğunu gösterebilir, ama bir sonrakinin de öyle olacağını gösteremez. Sayfa bunun yerine bunu söyler: tam sayılar bildirimi her sayının tam sayı olduğu her konumu listeler ve bir değerin tam sayı kalması gerektiğini bildiğiniz yerde ".int()" eklemek tek sözcüklük bir düzenlemedir. Bir sayının güvenli aralığın dışında bir tam sayı olduğu yerde bu bildirim verilmez, çünkü Zod 4’teki ".int()" o örneği bizzat reddederdi.
- Bir e-posta adresi neden yalın bir "z.string()" olarak çıkıyor?
- Çünkü örneğinizde bir e-postaya benzeyen bir dize bir sonraki hakkında hiçbir şey söylemez ve doğru bir biçim denetimi, Zod’un sürümler arasında değişen kendi desenlerini izlemek zorundadır — Zod 4’teki "z.uuid()", Zod 3’teki ".uuid()" yönteminin geçirdiği dizeleri zaten reddeder. Tarihler, URL’ler ve UUID’ler aynı nedenle dize olarak kalır ve birkaç değerden başka hiçbir şey tutmamış bir alan hiçbir zaman bir enum ya da bir değişmez olmaz. Kuralı biliyorsanız onu yazın; şema size ait olan sıradan bir koddur.
- Kendi örneğim neden Zod 4’te başarısız oluyor?
- JSON ayrıştırıldığında Infinity ya da -Infinity olmuş, bir JavaScript sayısının tutabileceğinin ötesinde bir sayı tutuyor — diyelim 1e999 — ve Zod 4’teki "z.number()" sonsuz bir sayıyı reddeder, Zod 3’teki ise kabul eder. Infinity bildirimi işin içindeki her konumu adıyla anar. Bunun dışında bir şema, çıktığı örneği her zaman kabul eder, çünkü örnekteki her değer ona girmiştir; depo bunu iki sürümde de bir örnek derlemi üzerinde denetler.
- Gönderdiğim bir alan ayrıştırılan sonuçtan neden kayboldu?
- Çünkü şema onu listelemiyor. "z.object", fazladan anahtarları olan bir nesneyi kabul eder ve iki sürümde de onu bu anahtarlar olmadan döndürür ve örneğinizde bulunmayan bir anahtar, şemanın hiç öğrenmediği bir anahtardır. Onu şemaya ekleyin ya da bilinmeyen anahtarlar dokunulmadan geçmeliyse o tek nesneyi gevşek yapın — Zod 4’te "z.looseObject", Zod 3’te ".passthrough()".
- Bir şemayı başka birinin altına taşıdım ve bir ReferenceError aldım. Neden?
- Çünkü bir şema bir değerdir ve JavaScript bir değerin, onu tanımlayan satırın üstünde okunmasına izin vermez. Sayfa, tam da bu nedenle, her şemayı kullandığı her şemadan sonra, kökü de en sona koyarak yazdırır; bir şemayı ona başvuran her şeyin üstünde tutun ve hata ortadan kalkar.
- Şema neden CustomerSchema değil de Customer diye adlandırılıyor?
- Bu, zod.dev’in kendi adlandırma kuralıdır: bir şema ve çıkardığı tür tek bir adı paylaşır; TypeScript buna izin verir, çünkü değerler ve türler ayrı ad alanlarında yaşar. Adın kendisi, JSON’dan TypeScript’e sayfasının aynı nesneye verdiği addır; bu yüzden iki sayfada da, şema için de türü için de aynı sözcüktür.
- Çıktı Zod’un hangi sürümü için yazılmıştır?
- Zod 4 için, Zod 3’te bulunmayan hiçbir şeyi kullanmadan; bu yüzden ikisinde de değişmeden çalışır. İkisi birkaç yerde ayrılır — sonsuz bir sayı, derinlik sınırının ötesindeki bir anahtar, "__proto__" diye yazılan bir anahtar ve çok derine iç içe geçmiş bir dizinin türü — ve her biri yukarıda açıklanmıştır. Zincirleme yöntemlerle olağan Zod’dur; Zod Mini aynı şemayı işlevlerle yazar ve onu olduğu hâliyle çalıştıramaz.
- Kimlik bilgileri dahil gerçek veri yapıştırabilir miyim?
- Evet. Şema tarayıcınızda hesaplanır: yapıştırdığınız şey kendi makinenizde okunur ve bir sunucuya gönderilmez, saklanmaz ya da kaydedilmez. Şema ayrıca değerlerinizin hiçbirini tutmaz, yalnızca anahtarlarınızı ve her birinin altındaki değerin türünü tutar; bu yüzden örnekteki bir token "z.string()" olarak çıkar, o kadar.
İlgili araçlar
- JSON’dan TypeScript’e
Bu sayfadaki bir şema kodunuzun parçası olarak çalışır ve veriyi her geldiğinde denetler. O sayfa aynı JSON için aynı cevabı aynı adlarla yalın TypeScript türleri olarak verir; bunlar kodunuz derlenirken denetlenir ve çalışma zamanında hiçbir şey eklemez.
- JSON’dan Go’ya
Bu sayfa, örneğinizdeki her sayının tam sayı olduğu yerde bile tam sayı gerektirmeyi size bırakır. O sayfa aynı biçimi aynı tür adlarıyla çıkarır; ancak Go’da yalnızca bir JSON sayısı olan bir tür yoktur, bu yüzden her sayının tam sayı olarak yazıldığı yerde alana bir tam sayı türü verir ve Go, ondalık noktası içeren bir değeri bu türe almayı reddeder.
- JSONPath test aracı
RFC 9535 JSONPath sorgularını JSON’a karşı test edin.
- Markdown tablo oluşturucu
CSV, TSV veya JSON verisinden Markdown tablosu oluşturun.