はじめに
こんにちは、株式会社TOKOSのツキヤです!
2025年の7月に、zodのv4がリリースされましたね🚀
書き方が変わったり、便利なスキーマが追加されたりしました✨
そこで、既にあるが書き方等が変わったスキーマと、新規で便利そうなスキーマ達を紹介しようと思います💪
v3からのマイグレーションガイドも出ているので、一旦バージョンアップだけしたい方はそちらを参考にしてみてください!
オススメのスキーマ
まずは、v3以前から有ったスキーマ達です!
z.int()
皆さん、number
がほしいときにフワッとz.number()
にしていませんか?z.number()
だと、少数等も含まれてしまいます😨
明確に「整数」が良い時はz.int()
を使いましょう!
import { z } from "zod"
const schema = z.int()
schema.parse(1) // 成功!
schema.parse(0.1) // エラー!
こちらはv3から有りましたが、z.number().int()
と書く必要がありました。
v4からはnumber()
の宣言ナシにそのままz.int()
と呼べるようになったので覚えておきましょう💪
また、「整数かつ 正の数」が良い場合は、z.int().positive()
とも書けます!
import { z } from "zod"
const schema = z.int().positive()
schema.parse(1) // 成功!
schema.parse(0.1) // エラー!
schema.parse(-1) // エラー!
z.email()
メールアドレスのバリデーションを正規表現で頑張って設定してたりしてないですか??
そんな時に使えるのがz.email()
です!
こちらは、一般的なメールアドレスの正規表現をカバーしてくれています✨
z.email()
についても、v3からありました。ですが、こちらもz.string().email()
のstring()
が不要になっています。
また、カスタムもできるようになりました!!
細かく調整したい方はこちらも参照してください。
import { z } from "zod"
/*
* Zodのデフォルトemailバリデーション(Gmail規則)
* colinhacks.com/essays/reasonable-email-regex を参照
*/
z.email()
/*
* ブラウザがinput[type=email]フィールドの検証に使用する正規表現
* https://developer.mozilla.org/en-US/docs/Web/HTML/Element/input/email
*/
z.email({ pattern: z.regexes.html5Email })
// RFC 5322に準拠したクラシックなemailregex.comの正規表現
z.email({ pattern: z.regexes.rfc5322Email })
// Unicodeを許可する緩い正規表現(国際化メール対応)
z.email({ pattern: z.regexes.unicodeEmail })

他のz.string()
系と併用していたスキーマも、同じくstring()
が不要になっているので合わせて覚えておきたいです!
extend()
こちらも元からあるもので、スキーマ同士を合成させるような働きでした。
ですが、merge()
との違いがよく分かってませんでした🤔
import { z } from "zod"
const humanSchema = z.object({
name: z.string().min(1),
age: z.number().int().positive(),
})
const extendProgramerSchema = humanSchema.extend({
occupation: z.string().min(1),
})
const mergeProgramerSchema = humanSchema.merge(z.object({
occupation: z.string().min(1),
}))
type Human = z.infer<typeof humanSchema>
// => { name: string; age: number }
type Programer = z.infer<typeof extendProgramerSchema>
// => { name: string; age: number; occupation: string } 🤔
type Programer = z.infer<typeof mergeProgramerSchema>
// => { name: string; age: number; occupation: string } 🤔
そういった背景もあってか、v4からはmerge()
の方が非推奨になりました!
これで自信を持ってextend()
を使えるので、皆さん覚えておきましょう🥳
…ただ、スキーマの合成の際のベストプラクティスは「shape
+ スプレッド構文」になったようです!
スプレッド構文を用いた方法が1番パフォーマンスが良いらしいので、僕は極力こちらを使うようにします!
import { z } from "zod"
const humanSchema = z.object({
name: z.string().min(1),
age: z.number().int().positive(),
})
const occupationSchema = z.object({
occupation: z.string().min(1),
})
const programerSchema = z.object({
...humanSchema.shape,
...occupationSchema.shape
})
type Programer = z.infer<typeof extendProgramerSchema>
// => { name: string; age: number; occupation: string } 🥳
refine()
こちらもv3から存在します!
皆さん1回は使ったことがあると思っていて、カスタムのバリデーションを書けるやつですね!
import { z } from "zod"
z.string().refine(val => val.includes("@")) // 「@」が含まれていないとバリデーションエラー!
ただ、v3まではrefine()
後にmin()
等のスキーマを追加することができませんでした🥲
import { z } from "zod"
z.string().refine(val => val.includes("@")).min(10) // 型エラー🥲
ですが、v4からは可能になりました✨
import { z } from "zod"
z.string().refine(val => val.includes("@")).min(10) // 型エラーにならない🥳
次からは新しいスキーマの紹介です!!
z.templateRiteral
説明するよりコードを読んだ方が分かりやすいので記載します!
import { z } from "zod"
const email = z.templateLiteral([
z.string().min(1),
"@",
z.string().max(64),
]);
type Email = z.infer<typeof email>
// => `${string}@${string}`というテンプレートリテラル型になる✨
配列の要素をまとめた上でテンプレートリテラル型になってくれる感じです!
パッといい感じの具体例は思い浮かびませんが、覚えておくとどこかしらでつかえそうです💪
(上記の例は、z.email()
で代用できそうなので…)
z.stringbool()
文字列からいい感じにbooleanに変換したい時ありますよね?
例えば、文字列の"true"
をbooleanのtrue
にしたい時とかです!
そんな時に使えるのがこちらのstringbool()
です!
import { z } from "zod"
const strbool = z.stringbool();
strbool.parse("true") // => true
strbool.parse("1") // => true
strbool.parse("yes") // => true
strbool.parse("on") // => true
strbool.parse("y") // => true
strbool.parse("enabled") // => true
strbool.parse("false"); // => false
strbool.parse("0"); // => false
strbool.parse("no"); // => false
strbool.parse("off"); // => false
strbool.parse("n"); // => false
strbool.parse("disabled"); // => false
こんな感じで、true
っぽい文字列はtrue
に、false
っぽい文字列はfalse
になります!
ただ、「自分で決めたいぜ!」って人もいると思います。
そんな人のためにも、自分で定義することが可能になっています。
import { z } from "zod"
const strbool = z.stringbool({
truthy: ["yes", "true", "hoge"],
falsy: ["no", "false", "huga"]
})
strbool.parse("yes") // => true
strbool.parse("true") // => true
strbool.parse("hoge") // => true
strbool.parse("no"); // => false
strbool.parse("false"); // => false
strbool.parse("huga"); // => false
おわりに
今回は、v4になったzodの紹介をしました!
破壊的な変更も多く、この他にも便利になったり新しい機能等がいっぱい有るので、ぜひご自身でも確認してみてください!