# zod筆記 ## zod schema vs interface ### interface缺陷 只能做型別檢測,如遇到遇到data是空的ts並不會幫你檢測出來,但zod可以 ```jsonld= //../data/data.json { "results":[ { "id":0, "name":"danny", "job":"develop" } ] } ``` ```jsonld= //../data/data2.json { } ``` ```javascript= import fs from "fs"; import { z, ZodSchema } from "zod"; const ResultSchema = z.object({ results: z.array( z.object({ id: z.number(), name: z.string(), job: z.string(), }) ) } ) type ResultZod = z.infer<typeof ResultSchema> interface Result { results: { id: number name: string job: string }[] } //ResultZod 和 Result型別一樣 function PrintResultWithOutZod(result: Result) { result.results.forEach((result) => { console.log(result.job) }) } function PrintResultWithZod(result: ResultZod) { if (ResultSchema.safeParse(result).success) { console.log('Good data') result.results.forEach((result) => { console.log(result.job) }) } else { console.log('Bad data') } } const result: Result = JSON.parse(fs.readFileSync('../data/data2.json', "utf-8")) console.log(result) PrintResultWithZod(result) const InfoSchema = z.object({ name: z.string(), age: z.number().min(20) }) function CustomerSchemaValidate<T>(schema: z.Schema<T>, value: T) { const data = schema.safeParse(value) if (data.success) { console.log('success') } else { console.log('error') } } CustomerSchemaValidate(InfoSchema, { name: 'danny', age: 20 }) ``` ## 比較error! ### 用 interface ![](https://i.imgur.com/9wFchtJ.png) ### 用zod ![](https://i.imgur.com/xCjGst8.png) **是不是代碼很簡潔呢** ## 表單驗證與zod zod的特性很特別的在於可以自定義error message,利用superRefine去判斷schema欄位中是否有等值,如果錯誤可以自定義error message ```typescript= import { z, ZodError, ZodIssueCode } from "zod"; const LoginFormSchema = z.object({ email: z.string().email(), password: z.string().min(4), confirmPassword: z.string().min(4), remember: z.boolean().default(false).optional(), }).superRefine(({ confirmPassword, password }, ctx) => { if (password !== confirmPassword) { ctx.addIssue({ code: 'custom', message: 'The passwords did not match', path: ["confirmPassword"] }) } }) const LoginFormSchema2 = z.object({ email: z.string().email(), password: z.string().min(4), confirmPassword: z.string().min(4), remember: z.boolean().default(false).optional(), }).refine( ({ confirmPassword, password }) => { return confirmPassword === password }, { path: ["confirmPassword"], message: "Passwords don't match", } ) const useForm = <TValues>(schema: z.Schema<TValues>, onSubmit: (values: TValues) => void) => { return { onSubmit: (value: unknown) => { try { const newValue = schema.parse(value) onSubmit(newValue) } catch (e) { if (e instanceof ZodError) { const { message, path } = e.errors[0] console.log(path + ': ' + message) } } } } } const form = useForm(LoginFormSchema, (values) => { console.log(values) }) const password = { email: 'hiunji64@gmail.com', password: '1111', confirmPassword: '1111', } form.onSubmit(password) // form.checkoutError(password) ``` ## 用schema取特定data ```typescript= import { z } from "zod"; const commentSchema = z.object({ email: z.string() }) const commentSchemaResult = z.array( commentSchema ) const genericFetch = <ZSchema extends z.ZodSchema>( url: string, schema: ZSchema ): Promise<z.infer<ZSchema>> => { return fetch(url) .then(res => res.json()) .then(result => schema.parse(result)) } const getComment = async () => { const aaa = await genericFetch('https://jsonplaceholder.typicode.com/comments?_page=2', commentSchemaResult) // aaa.forEach(item=>{ // console.log(item.email) // }) } getComment() ``` ```typescript //origin data { "postId": 3, "id": 11, "name": "fugit labore quia mollitia quas deserunt nostrum sunt", "email": "Veronica_Goodwin@timmothy.net", "body": "ut dolorum nostrum id quia aut est\nfuga est inventore vel eligendi explicabo quis consectetur\naut occaecati repellat id natus quo est\nut blanditiis quia ut vel ut maiores ea" } //need data { "email": "Veronica_Goodwin@timmothy.net", } ``` ## 甚至你還可以自訂欄位 ```typescript= const StarWarsPerson = z.object({ name: z.string(), }).transform((person) => ({ ...person, nameAsArray: person.name.split(" ") })) const StarWarsPeopleResults = z.object({ results: z.array(StarWarsPerson) }) const fetchStarWarsPeople = async () => { const data = await fetch( "https://www.totaltypescript.com/swapi/people.json", ).then((res) => res.json()); const parseData = StarWarsPeopleResults.parse(data) console.log(parseData) // parseData.results.forEach(item=>{ // console.log(item.name) // }) return parseData } fetchStarWarsPeople() ``` ## JSON type ```typescript const jsonSchema = z.string().transform(async (string, ctx) => { try { return JSON.parse(string) } catch (e) { ctx.addIssue({ code: 'custom', message: 'Invalidate JSON' }) return z.never } }) ``` ## reference [官方文件](https://zod.dev/)