# 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/)