sinclairzx81 / typebox

Json Schema Type Builder with Static Type Resolution for TypeScript
Other
4.95k stars 155 forks source link

How to do a cross field validation? #934

Closed askareija closed 2 months ago

askareija commented 2 months ago

Hi!, i have a question. I'm using elysia and i know that elysia using typebox for validation. I have validation code like this:

export const authRequestVerificationCodeValidation = {
  body: t.Object({
    contact: t.Union([
      t.String({ format: "email" }),
      t.String({ minLength: 8 }),
    ]),
    sendMethod: t.Union([t.Literal("email"), t.Literal("whatsapp")]),
  }),
};

Is there a way i can validate if the sendMethod field is email, then contact field validation must be t.String with email format, else if whatsapp then the validation is t.String with minLength of 8?

Thanks

sinclairzx81 commented 2 months ago

@askareija Hi,

TypeBox doesn't have cross field validation as it's not well represented in the Json Schema specification. However you can reimplement this type in the following way to make use of TypeScript discriminated unions (which is supported in TypeBox)

TypeScript Link Here

import { Type as t, Static } from '@sinclair/typebox'

export type AuthRequestVerificationType = Static<typeof AuthRequestVerificationType>
export const AuthRequestVerificationType = t.Union([
  t.Object({ 
    sendMethod: t.Literal('email'),    // discriminator
    contact: t.String({ format: 'email' })
  }),
  t.Object({ 
    sendMethod: t.Literal('whatsapp'), // discriminator
    contact: t.String({ minLength: 8 })
  }),
])

Hope this helps S

sinclairzx81 commented 2 months ago

Just as follow on addendum, for your particular case, the specification does support conditional schematics which would work for your particular use case...

import { Type, Static } from '@sinclair/typebox'

export const T = Type.Object({
    x: Type.Union([
        Type.Literal(1),
        Type.Literal(2),
    ]),
}, {
    if: Type.Object({ x: Type.Literal(1) }),
    then: Type.Object({ y: Type.Literal(3) })
})

// if x === 1 expect { x: 1, y: '...' }
// if x === 2 expect { x: 2 }

However the TypeBox compiler infrastructure doesn't currently support these kind of schematics as there are difficulties reconciling the appropriate static type (although the above schematic is supported in Ajv and other standard validators). As Elysia is backed by the TypeBox TypeCompiler, the recommendation is to use the Discriminated Union type above.

Cheers S

askareija commented 2 months ago

Ah i see, thanks! i'll try this