Skip to content

Instantly share code, notes, and snippets.

@plourenco
Created December 1, 2022 21:05
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save plourenco/0ec255191dfd75789e3d5a396c06696b to your computer and use it in GitHub Desktop.
Save plourenco/0ec255191dfd75789e3d5a396c06696b to your computer and use it in GitHub Desktop.
Validator comparison
/* eslint-disable no-console */
import Ajv, { JTDDataType } from "ajv/dist/jtd";
import Joi from "joi";
import { z } from "zod";
import yup, { ValidationError } from "yup";
export type AuthResponse = {
investorId: string;
expiration: number;
};
// Joi
// Advantage: quite popular, straightforward, organization
// Disadvantage: still requires types (AuthResponse)
export const joiSchema = Joi.object({
investorId: Joi.string().required(),
expiration: Joi.number().optional(),
});
// .options({ presence: 'required' }) may add required by default
const joiInput = { investorId: "pedro" };
const result = joiSchema.validate(joiInput);
console.log(result.value); // value is any
console.log((joiInput as AuthResponse).investorId); // needs to be casted
// --------------------------------------------------------------------------------
// Ajv
// Advantage: quite popular; TS-friendly: generates types within validation scope;
// uses JSON schema standard, generic, organization sponsored by mozilla/microsoft
// Disadvantage: somewhat complex
export const schema = {
type: "object",
properties: {
investorId: { type: "string" },
expiration: { type: "number" },
},
required: ["investorId"],
};
// self generated typings
type AjvAuthResponse = JTDDataType<typeof schema>;
const ajv = new Ajv(); // this Ajv instance would be used for the whole application
const validate = ajv.compile<AjvAuthResponse>(schema);
const ajvInput = { investorId: "pedro" };
validate(ajvInput); // boolean
if (validate(ajvInput)) {
// from here on, the type of ajvInput is like schema
console.log(ajvInput.expiration);
} else {
console.log(validate.errors); // any errors, can be changed to a single error too
}
// --------------------------------------------------------------------------------
// Zod
// Advantage: TS-friendly: parsing, so no types required, fields required by default
// Disadvantage: not an organization, one maintainer, not very mature (2020)
const zodSchema = z.object({
investorId: z.string(),
expiration: z.number().optional(),
});
try {
const zodTest = zodSchema.parse({ investorId: "pedro" });
console.log(zodTest.investorId); // type is already of same shape
// console.log(zodTest.foo); // TS error - property does not exist
} catch (err) {
if (err instanceof z.ZodError) {
console.log(err.message);
}
}
// --------------------------------------------------------------------------------
// Yup
// Advantage: TS-friendly: generates types, from 2014
// Disadvantage: not an organization, mostly run by one maintainer
const yupSchema = yup.object({
investorId: yup.string(),
expiration: yup.number(),
});
try {
const yupResult = yupSchema.validateSync({ investorId: "pedro" });
console.log(yupResult.expiration); // type is already of same shape
// console.log(zodTest.foo); // TS error - property does not exist
} catch (err) {
if (err instanceof ValidationError) {
console.log(err.message);
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment