'Fastest-Validator. Create schema with dependency on value

I have two user types - teacher and student.
Teacher schema is:

{
   username : string;
   firstName : string;
   lastName : string;
   type : 1; // 1 = teacher
   schoolId : objectId;
   email : string;
}

Student schema is:

{
   username : string;
   firstName : string;
   lastName : string;
   type : 2; // 2 = student
   classCode : number; 
}

For validation I use fastest-validator. I want to build one schema for it. But if you see, we have some different fields in different types of users (teacher has email and schoolId; student has classCode).
My wish is to build some schema like following pseudocode:

{
  username : string,
  lastName : string,
  firstName: string,
  or: [
    { type : 1, ... }, 
    { type : 2, ... }
  ]
}


Solution 1:[1]

Please use Json-Schema as it follows standards, it is fast and it has no dependencies.

You can see bellow ObjectTypeDef which helps to produce a correct schema.

The functions isTeacher, isStudent and isTeacherOrStudent will type your object as parameter if function returns true.

The property anyOfis the best answer for you: a schema should be all good, else is false. The conditions should wrap every allowed schemas.

import type { JSONSchema7, JSONSchema7Definition } from 'json-schema';
import { validate } from 'json-schema';

type AnyObject<K extends string | number | symbol = string | number | symbol, T = any> = Record<K, T>;

type ObjectTypeDef<T extends AnyObject> = JSONSchema7 & {
  properties: Record<keyof T, JSONSchema7Definition>;
  required: (keyof T)[];
};

type Teacher = {
  username: string;
  firstName: string;
  lastName: string;
  type: 1;
  schoolId: string;
  email: string;
};

type Student = {
  username: string;
  firstName: string;
  lastName: string;
  type: 2;
  classCode: number;
};

const schemaTeacher: ObjectTypeDef<Teacher> = {
  properties: {
    username: { type: 'string' },
    firstName: { type: 'string' },
    lastName: { type: 'string' },
    type: { const: 1 },
    schoolId: { type: 'string' },
    email: { type: 'string' },
  },
  required: ['username', 'firstName', 'lastName', 'type', 'schoolId', 'email'],
  additionalProperties: false,
};

const schemaStudent: ObjectTypeDef<Student> = {
  properties: {
    username: { type: 'string' },
    firstName: { type: 'string' },
    lastName: { type: 'string' },
    type: { const: 2 },
    classCode: { type: 'number' },
  },
  required: ['username', 'firstName', 'lastName', 'type', 'classCode'],
  additionalProperties: false,
};

export const isTeacher = (obj: AnyObject): obj is Teacher => validate(obj, schemaTeacher).valid;
export const isStudent = (obj: AnyObject): obj is Student => validate(obj, schemaStudent).valid;

const schemaTeacherOrStudent: JSONSchema7 = {
  anyOf: [schemaStudent, schemaTeacher],
};

export const isTeacherOrStudent = (obj: AnyObject): obj is Teacher | Student =>
  validate(obj, schemaTeacherOrStudent).valid;

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1 karkael