CtrlK
BlogDocsLog inGet started
Tessl Logo

tessl/npm-yup

Dead simple Object schema validation

Overview
Eval results
Files

validation.mddocs/

Validation & Testing

Comprehensive validation system with async/sync validation, custom tests, conditional validation, and detailed error handling for robust data validation scenarios.

Capabilities

Core Validation Methods

Primary methods for validating data against schemas, available on all schema types.

/**
 * Asynchronously validate a value against the schema
 * @param value - Value to validate
 * @param options - Validation configuration options
 * @returns Promise resolving to validated and transformed value
 * @throws ValidationError if validation fails
 */
validate(value: any, options?: ValidateOptions): Promise<T>;

/**
 * Synchronously validate a value against the schema
 * @param value - Value to validate
 * @param options - Validation configuration options
 * @returns Validated and transformed value
 * @throws ValidationError if validation fails
 */
validateSync(value: any, options?: ValidateOptions): T;

/**
 * Validate a nested field at a specific path within an object
 * @param path - Dot-notation path to the field
 * @param value - Root object containing the field
 * @param options - Validation configuration options
 * @returns Promise resolving to validated field value
 */
validateAt(path: string, value: any, options?: ValidateOptions): Promise<any>;

/**
 * Synchronously validate a nested field at a specific path
 * @param path - Dot-notation path to the field
 * @param value - Root object containing the field
 * @param options - Validation configuration options
 * @returns Validated field value
 */
validateSyncAt(path: string, value: any, options?: ValidateOptions): any;

/**
 * Check if a value is valid without throwing errors
 * @param value - Value to check
 * @param options - Validation configuration options
 * @returns Promise resolving to boolean validity status
 */
isValid(value: any, options?: ValidateOptions): Promise<boolean>;

/**
 * Synchronously check if a value is valid without throwing errors
 * @param value - Value to check
 * @param options - Validation configuration options
 * @returns Boolean validity status
 */
isValidSync(value: any, options?: ValidateOptions): boolean;

/**
 * Check if a value matches the schema's expected type
 * @param value - Value to type-check
 * @returns Type predicate indicating if value matches schema type
 */
isType(value: any): value is T;

interface ValidateOptions<TContext = {}> {
  /** Enable strict validation (no type coercion) */
  strict?: boolean;
  /** Stop validation on first error (default: true) */
  abortEarly?: boolean;
  /** Remove unknown fields from objects */
  stripUnknown?: boolean;
  /** Validate nested schemas recursively */
  recursive?: boolean;
  /** Additional context data available during validation */
  context?: TContext;
}

Usage Examples:

import { object, string, number } from "yup";

const userSchema = object({
  name: string().required(),
  age: number().positive().integer(),
});

// Async validation
try {
  const user = await userSchema.validate({
    name: "John",
    age: 25
  });
  console.log(user); // { name: "John", age: 25 }
} catch (error) {
  console.error(error.errors); // Array of error messages
}

// Sync validation
try {
  const user = userSchema.validateSync({ name: "John", age: 25 });
} catch (error) {
  console.error(error.message);
}

// Check validity without throwing
const isValid = await userSchema.isValid({ name: "John", age: -5 }); // false

// Validate nested field
await userSchema.validateAt("name", { name: "John", age: 25 }); // "John"

Value Casting

Transform and coerce values to match schema types without full validation.

/**
 * Cast/transform a value to match the schema type
 * @param value - Value to cast
 * @param options - Casting configuration options
 * @returns Transformed value matching schema type
 */
cast(value: any, options?: CastOptions): T;

interface CastOptions<TContext = {}> {
  /** Enable strict casting (minimal transformations) */
  strict?: boolean;
  /** Remove unknown fields from objects */
  stripUnknown?: boolean;
  /** Additional context data available during casting */
  context?: TContext;
}

Usage Examples:

import { string, number, date } from "yup";

// String casting
const stringSchema = string();
stringSchema.cast(123); // "123"
stringSchema.cast(true); // "true"

// Number casting  
const numberSchema = number();
numberSchema.cast("42"); // 42
numberSchema.cast("3.14"); // 3.14

// Date casting
const dateSchema = date();
dateSchema.cast("2023-01-01"); // Date object
dateSchema.cast(1640995200000); // Date from timestamp

Custom Validation Tests

Add custom validation logic with detailed error reporting and context access.

/**
 * Add a custom validation test to the schema
 * @param name - Test name for error reporting
 * @param message - Error message when test fails
 * @param testFn - Function that returns true if valid
 * @returns New schema with custom test
 */
test(name: string, message: string, testFn: TestFunction): Schema;

/**
 * Add a custom validation test with full configuration
 * @param options - Complete test configuration
 * @returns New schema with custom test
 */
test(options: TestConfig): Schema;

type TestFunction<T = any, C = any> = (
  value: T,
  context: TestContext<T, C>
) => boolean | Promise<boolean>;

interface TestConfig<T = any, C = any> {
  /** Unique name for the test */
  name: string;
  /** Error message when test fails (string or function) */
  message: string | ((params: TestMessageParams) => string);
  /** Test function that validates the value */
  test: TestFunction<T, C>;
  /** Skip test if value is empty/null/undefined */
  skipAbsent?: boolean;
  /** Test is exclusive - removes other tests with same name */
  exclusive?: boolean;
  /** Parameters to pass to error message function */
  params?: Record<string, any>;
}

interface TestContext<T = any, C = any> {
  /** Path to current field being validated */
  path: string;
  /** Field name being validated */
  key?: string;
  /** Parent object containing the field */
  parent: any;
  /** Root value being validated */  
  from: Array<{ schema: Schema; value: any }>;
  /** Additional context data */
  options: ValidateOptions<C>;
  /** Create ValidationError for this test */
  createError(params?: CreateErrorOptions): ValidationError;
  /** Resolve references and lazy schemas */
  resolve(value: any): any;
}

interface CreateErrorOptions {
  /** Override default error message */
  message?: string;
  /** Override field path */
  path?: string; 
  /** Override field value */
  value?: any;
  /** Additional parameters for message interpolation */
  params?: Record<string, any>;
}

Usage Examples:

import { string, number, object } from "yup";

// Simple custom test
const evenNumberSchema = number().test(
  "is-even",
  "Number must be even",
  (value) => value % 2 === 0
);

// Async custom test with API call
const uniqueEmailSchema = string().test(
  "unique-email",
  "Email already exists",
  async (email) => {
    const exists = await checkEmailExists(email);
    return !exists;
  }
);

// Custom test with context access
const passwordSchema = object({
  password: string().min(8),
  confirmPassword: string().test(
    "passwords-match",
    "Passwords must match",
    function(value) {
      return value === this.parent.password;
    }
  ),
});

// Custom test with dynamic message
const minLengthSchema = string().test({
  name: "min-length",
  message: ({ min }) => `Must be at least ${min} characters`,
  params: { min: 5 },
  test: (value) => value && value.length >= 5,
});

Conditional Validation

Create dynamic validation rules based on other field values or conditions.

/**
 * Apply conditional validation based on other fields
 * @param keys - Field name(s) to check for condition
 * @param builder - Function or config that returns schema based on condition
 * @returns New schema with conditional validation
 */
when<U extends Schema>(
  keys: string | string[],
  builder: WhenBuilder<U> | WhenBuilderOptions<U>
): Schema;

type WhenBuilder<U extends Schema> = (
  value: any,
  schema: Schema
) => U;

interface WhenBuilderOptions<U extends Schema> {
  /** Value(s) to match against */
  is?: any | any[];
  /** Schema to use when condition matches */
  then?: U | ((schema: Schema) => U);
  /** Schema to use when condition doesn't match */
  otherwise?: U | ((schema: Schema) => U);
}

Usage Examples:

import { object, string, boolean, number } from "yup";

// Simple conditional validation
const schema = object({
  isBusiness: boolean(),
  companyName: string().when("isBusiness", {
    is: true,
    then: (schema) => schema.required("Company name is required"),
    otherwise: (schema) => schema.strip(),
  }),
});

// Multiple conditions
const subscriptionSchema = object({
  type: string().oneOf(["free", "premium", "enterprise"]),
  seats: number().when("type", {
    is: "enterprise", 
    then: (schema) => schema.min(10).required(),
    otherwise: (schema) => schema.max(5),
  }),
  customDomain: string().when("type", (type, schema) => {
    return type === "premium" || type === "enterprise"
      ? schema.required()
      : schema.strip();
  }),
});

// Multiple field dependencies
const addressSchema = object({
  country: string().required(),
  state: string().when("country", {
    is: "US",
    then: (schema) => schema.required(),
  }),
  zipCode: string().when(["country", "state"], {
    is: (country, state) => country === "US" && state,
    then: (schema) => schema.matches(/^\d{5}$/, "Invalid ZIP code"),
  }),
});

Error Handling

Comprehensive error information with detailed validation failure reporting.

/**
 * Error thrown when validation fails
 */
class ValidationError extends Error {
  /** Always "ValidationError" */
  name: "ValidationError";
  /** Primary error message */
  message: string;
  /** Value that failed validation */
  value: any;
  /** Path where validation failed */
  path?: string;
  /** Type of validation that failed */
  type?: string;
  /** Parameters used in error message */
  params?: Record<string, any>;
  /** Array of all error messages */
  errors: string[];
  /** Array of nested ValidationError objects */
  inner: ValidationError[];

  /**
   * Format error message with parameters
   * @param message - Message template
   * @param params - Parameters for interpolation
   * @returns Formatted message
   */
  static formatError(
    message: string | ((params: any) => string),
    params: Record<string, any>
  ): string;

  /**
   * Check if an error is a ValidationError
   * @param err - Error to check
   * @returns Type predicate for ValidationError
   */
  static isError(err: any): err is ValidationError;
}

Usage Examples:

import { object, string, ValidationError } from "yup";

const schema = object({
  name: string().required("Name is required"),
  email: string().email("Invalid email format").required(),
});

try {
  await schema.validate({ name: "", email: "not-email" });
} catch (error) {
  if (ValidationError.isError(error)) {
    console.log("Validation failed!");
    console.log("Primary message:", error.message);
    console.log("All errors:", error.errors);
    console.log("Failed at path:", error.path);
    console.log("Failed value:", error.value);
    
    // Access nested errors
    error.inner.forEach((nestedError) => {
      console.log(`${nestedError.path}: ${nestedError.message}`);
    });
  }
}

// Custom error handling
const handleValidationError = (error: ValidationError) => {
  const fieldErrors: Record<string, string> = {};
  
  error.inner.forEach((err) => {
    if (err.path) {
      fieldErrors[err.path] = err.message;
    }
  });
  
  return fieldErrors;
};

Transformation Functions

Apply custom transformations to values during validation and casting.

/**
 * Add a transformation function to the schema
 * @param transformFn - Function to transform the value
 * @returns New schema with transformation applied
 */
transform<U>(transformFn: TransformFunction<T, U>): Schema<U>;

type TransformFunction<T, U> = (
  currentValue: T,
  originalValue: any,
  context: TransformContext
) => U;

interface TransformContext {
  /** Path to current field */
  path: string;
  /** Parent object */
  parent: any;
  /** Root value being transformed */
  root: any;
  /** Additional context data */
  options: CastOptions;
}

Usage Examples:

import { string, number, array } from "yup";

// String transformation
const slugSchema = string()
  .transform((value) => value?.toLowerCase().replace(/\s+/g, "-"))
  .matches(/^[a-z0-9-]+$/, "Invalid slug format");

// Number transformation
const currencySchema = string()
  .transform((value) => {
    // Remove currency symbols and convert to number
    const cleaned = value?.replace(/[$,]/g, "");
    return parseFloat(cleaned) || 0;
  })
  .transform((value) => Math.round(value * 100) / 100); // Round to 2 decimals

// Array transformation
const csvToArraySchema = string()
  .transform((value) => value?.split(",").map(s => s.trim()))
  .transform((arr) => arr?.filter(Boolean)); // Remove empty strings

// Context-aware transformation
const fullNameSchema = object({
  firstName: string().required(),
  lastName: string().required(),
  fullName: string().transform(function(value, originalValue, context) {
    // Auto-generate full name if not provided
    if (!value && context.parent) {
      return `${context.parent.firstName} ${context.parent.lastName}`;
    }
    return value;
  }),
});

Metadata and Labeling

Add metadata and labels to schemas for enhanced error reporting and documentation.

/**
 * Set or get metadata for the schema
 * @param metadata - Metadata object to set (optional)
 * @returns Schema metadata or new schema with metadata
 */
meta(): Record<string, any> | undefined;
meta(metadata: Record<string, any>): Schema;

/**
 * Set a human-readable label for better error messages
 * @param label - Human-readable field label
 * @returns New schema with label
 */
label(label: string): Schema;

/**
 * Set custom error message for type validation failures
 * @param message - Custom type error message
 * @returns New schema with custom type error
 */
typeError(message: string): Schema;

/**
 * Get a description of the schema structure and rules
 * @param options - Description options
 * @returns Schema description object
 */
describe(options?: DescribeOptions): SchemaDescription;

interface DescribeOptions {
  /** Include field values in description */
  value?: any;
  /** Additional context */
  context?: any;
}

interface SchemaDescription {
  /** Schema type */
  type: string;
  /** Human-readable label */
  label?: string;
  /** Schema metadata */
  meta?: Record<string, any>;
  /** Whether field is required */
  optional: boolean;
  /** Whether field is nullable */
  nullable: boolean;
  /** Default value */
  default?: any;
  /** Applied tests and validations */
  tests: Array<{ name: string; params?: any }>;
  /** Inner type for arrays/objects */
  innerType?: SchemaDescription;
  /** Object fields */
  fields?: Record<string, SchemaDescription>;
}

Usage Examples:

import { string, object } from "yup";

// Schema with labels and metadata
const userSchema = object({
  email: string()
    .email()
    .required()
    .label("Email Address")
    .meta({ 
      category: "contact",
      sensitive: true,
      helpText: "We'll use this to send you updates"
    }),
  
  username: string()
    .matches(/^[a-zA-Z0-9_]+$/)
    .required()
    .label("Username")
    .typeError("Username must be a string")
    .meta({ category: "identity" }),
});

// Access metadata
const emailMeta = userSchema.fields.email.meta();
console.log(emailMeta.helpText); // "We'll use this to send you updates"

// Schema description
const description = userSchema.describe();
console.log(description.fields.email.label); // "Email Address"
console.log(description.fields.email.tests); // Array of validation tests

Install with Tessl CLI

npx tessl i tessl/npm-yup

docs

index.md

schema-types.md

utilities.md

validation.md

tile.json