0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

Zodの空文字チェック:string.nonemptyが公式Docの記載漏れ?

Posted at

Zodで空文字をチェックするなら .nonempty()、もうmin(1) はやめましょう

Zodで入力必須チェックをするとき、z.string().min(1, '〜') を使う例をよく見かけます。
でも、実は nonempty() というより直感的なメソッドもあります。


💡 nonempty() はZodの正式なメソッド

例えばこういう定義:

productCode: z.string().nonempty('商品コードは必須項目です')

これは以下と同じ意味です:

productCode: z.string().min(1, '商品コードは必須項目です')

Zodの公式ドキュメント(https://zod.dev/?id=strings)にはなぜか記載されていませんが、**ソースコードを見るとちゃんと存在しています**。

nonempty(message?: errorUtil.ErrMessage): ZodString;

入力値のチェックであれば空文字チェックだけで良いので、後で述べるnull,undefinedの考慮は要りません。

✅ 最小限のテストケース:空文字のチェック

it('should fail when productCode is missing', () => {
  const invalidData = { ...baseValidData, productCode: '' }
  const result = productSchema.safeParse(invalidData)
  expect(result.success).toBe(false)
  if (!result.success) {
    expect(result.error.format().productCode?._errors).toContain(
      '商品コードは必須項目です'
    )
  }
})

✅ テスト結果(min(1) / nonempty どちらも同じ)

 PASS  src/app/product/types/__tests__/product-scheme.test.ts
  productSchema - productCode
    ✓ should fail when productCode is missing (24 ms)

🤔 では undefinednull のときは?

次に undefinednull を渡してみると…

it('should fail when productCode is undefined', () => {
  const invalidData = { ...baseValidData, productCode: undefined as any }
  const result = productSchema.safeParse(invalidData)
  expect(result.success).toBe(false)
  if (!result.success) {
    expect(result.error.format().productCode?._errors).toContain(
      '商品コードは必須項目です'
    )
  }
})

it('should fail when productCode is null', () => {
  const invalidData = { ...baseValidData, productCode: null as any }
  const result = productSchema.safeParse(invalidData)
  expect(result.success).toBe(false)
  if (!result.success) {
    const errors = result.error.format().productCode?._errors
    expect(
      errors?.some(e => e.includes('必須')) || errors?.some(e => e.includes('string'))
    ).toBe(true)
  }
})

❌ テスト失敗(undefined のとき)

 FAIL  src/app/product/types/__tests__/product-scheme.test.ts
  productSchema - productCode
    ✓ should fail when productCode is missing
    ✕ should fail when productCode is undefined
    ✓ should fail when productCode is null

  ● should fail when productCode is undefined

    Expected value: "商品コードは必須項目です"
    Received array: ["Required"]

📌 なぜ undefined だけ違うエラー?

Zodでは、.string() の型バリデーションが先に走ります。
キーが存在しない(undefined)と「Missing value」とみなされ、Zodのデフォルトメッセージ "Required" が返ってきます。

一方で .min(1).nonempty() は「空文字に対する長さチェック」なので、そもそも undefined には到達しません


✅ 対策:required_error を指定する

productCode: z
  .string({ required_error: '商品コードは必須項目です' })
  .nonempty('商品コードは必須項目です')

これで、undefined のときも空文字のときも、同じメッセージで統一されます。

🔁 テスト再実行結果:

 PASS  src/app/product/types/__tests__/product-scheme.test.ts
  productSchema - productCode
    ✓ should fail when productCode is missing
    ✓ should fail when productCode is undefined
    ✓ should fail when productCode is null

✅ まとめ

入力値 .min(1) / .nonempty() 対応策
'' ❌ エラー(OK) メッセージ通る
null ❌ 型エラー nullable()で対応も可
undefined "Required" のまま required_error で統一しよう

✅ おまけ:どちらを使うべき?

  • 明示的に「空文字禁止」を書きたいなら nonempty() の方が読みやすい
  • より一般的なパターンや制約付き(例:min(5), max(10))があるなら min(x) の方が柔軟
  • チームで統一しておくのがベスト

🔚 おわりに

Zodの .nonempty() は便利なのに知られていない…!
これを機に min(1) だけでなく nonempty() の存在も知っておくと、コードがより意図的で読みやすくなります。

0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?