CtrlK
BlogDocsLog inGet started
Tessl Logo

tessl/npm-ts-interface-checker

Runtime library to validate data against TypeScript interfaces

Pending
Overview
Eval results
Files

error-handling.mddocs/

Error Handling

Comprehensive error reporting with path information, nested error details, and custom error types for validation failures.

Capabilities

VError Class

Custom error class for validation failures with path information.

/**
 * Error thrown by validation. Besides an informative message, it includes
 * the path to the property which triggered the failure.
 */
class VError extends Error {
  constructor(public path: string, message: string);
}

Usage Examples:

import { createCheckers, VError } from "ts-interface-checker";
import userTypes from "./user-ti";

const { User } = createCheckers(userTypes);

try {
  User.check({ name: "Alice" }); // Missing age property
} catch (error) {
  if (error instanceof VError) {
    console.log("Path:", error.path); // "value.age"
    console.log("Message:", error.message); // "value.age is missing"
    
    // Extract just the property path
    const propertyPath = error.path.replace(/^value\.?/, "");
    console.log("Property:", propertyPath); // "age"
  }
}

// Custom path reporting
User.setReportedPath("userData");
try {
  User.check({ name: "Bob" });
} catch (error) {
  if (error instanceof VError) {
    console.log("Path:", error.path); // "userData.age"
  }
}

IErrorDetail Interface

Detailed error information structure for comprehensive error reporting.

/**
 * Describes errors as returned by validate() and validateStrict() methods
 */
interface IErrorDetail {
  path: string;
  message: string;
  nested?: IErrorDetail[];
}

Usage Examples:

const { User } = createCheckers(userTypes);

const invalidData = {
  name: 123, // Should be string
  profile: {
    email: "not-an-email", // Invalid email format
    age: "thirty" // Should be number
  }
};

const errors = User.validate(invalidData);
if (errors) {
  function printErrors(errorList: IErrorDetail[], indent = 0) {
    for (const error of errorList) {
      const prefix = "  ".repeat(indent);
      console.log(`${prefix}${error.path}: ${error.message}`);
      
      if (error.nested) {
        printErrors(error.nested, indent + 1);
      }
    }
  }
  
  printErrors(errors);
  // Output:
  // value.name: is not a string
  // value.profile.email: is not a valid email
  // value.profile.age: is not a number
}

Error Context System

Internal context system for collecting and reporting validation errors.

/**
 * Context interface used during validation to collect error messages
 */
interface IContext {
  fail(relPath: string|number|null, message: string|null, score: number): false;
  unionResolver(): IUnionResolver;
  resolveUnion(ur: IUnionResolver): void;
  fork(): IContext;
  completeFork(): boolean;
  failed(): boolean;
}

The context system is used internally but understanding it helps with advanced error handling:

Context Types:

/**
 * Union resolver interface for handling union type validation errors
 */
interface IUnionResolver {
  createContext(): IContext;
}

/**
 * Fast implementation for boolean-only validation (no error messages)
 */
class NoopContext implements IContext, IUnionResolver {
  fail(relPath: string|number|null, message: string|null, score: number): false;
  fork(): IContext;
  completeFork(): boolean;
  failed(): boolean;
  unionResolver(): IUnionResolver;
  createContext(): IContext;
  resolveUnion(ur: IUnionResolver): void;
}

/**
 * Complete implementation that collects detailed error information
 */
class DetailContext implements IContext {
  static maxForks: number; // Maximum number of errors recorded at one level (default: 3)
  
  fail(relPath: string|number|null, message: string|null, score: number): false;
  fork(): IContext;
  completeFork(): boolean;
  failed(): boolean;
  unionResolver(): IUnionResolver;
  resolveUnion(ur: IUnionResolver): void;
  
  getError(path: string): VError;
  getErrorDetails(path: string): IErrorDetail[];
}

Error Handling Patterns

Common patterns for handling validation errors in applications.

Usage Examples:

import { createCheckers, VError, IErrorDetail } from "ts-interface-checker";

const { User } = createCheckers(userTypes);

// Basic error handling with try-catch
function validateUserData(data: unknown): User | null {
  try {
    User.check(data);
    return data as User;
  } catch (error) {
    if (error instanceof VError) {
      console.error(`Validation failed at ${error.path}: ${error.message}`);
    }
    return null;
  }
}

// Detailed error handling with validate()
function validateWithDetails(data: unknown): { 
  isValid: boolean; 
  user?: User; 
  errors?: IErrorDetail[] 
} {
  const errors = User.validate(data);
  
  if (errors) {
    return { isValid: false, errors };
  }
  
  return { isValid: true, user: data as User };
}

// API response error formatting
function formatValidationErrors(errors: IErrorDetail[]): string[] {
  function extractMessages(errorList: IErrorDetail[]): string[] {
    const messages: string[] = [];
    
    for (const error of errorList) {
      messages.push(`${error.path}: ${error.message}`);
      
      if (error.nested) {
        messages.push(...extractMessages(error.nested));
      }
    }
    
    return messages;
  }
  
  return extractMessages(errors);
}

// Express.js middleware example
function validateRequestBody(checkerName: string) {
  return (req: any, res: any, next: any) => {
    const checker = checkers[checkerName];
    const errors = checker.validate(req.body);
    
    if (errors) {
      return res.status(400).json({
        error: "Validation failed",
        details: formatValidationErrors(errors)
      });
    }
    
    next();
  };
}

Error Message Customization

Understanding error message structure for custom handling.

Error Message Types:

// Different error scenarios produce different message patterns:

// Missing required property
// Path: "value.age", Message: "is missing"

// Wrong type
// Path: "value.name", Message: "is not a string"

// Extra property (strict mode)
// Path: "value.extra", Message: "is extraneous"

// Union type mismatch
// Path: "value", Message: "is none of string, number"

// Array index error
// Path: "value[2]", Message: "is not a number"

// Nested object error
// Path: "value.profile.email", Message: "is not a string"

Usage Examples:

// Custom error message processing
function processError(error: VError): { field: string; type: string; message: string } {
  const path = error.path;
  const message = error.message;
  
  // Extract field name from path
  const field = path.replace(/^value\.?/, "") || "root";
  
  // Determine error type from message
  let type = "unknown";
  if (message.includes("is missing")) {
    type = "required";
  } else if (message.includes("is not a")) {
    type = "type";
  } else if (message.includes("is extraneous")) {
    type = "extra";
  } else if (message.includes("is none of")) {
    type = "union";
  }
  
  return { field, type, message };
}

// Usage
try {
  User.check({ name: 123 });
} catch (error) {
  if (error instanceof VError) {
    const processed = processError(error);
    console.log(processed);
    // { field: "name", type: "type", message: "value.name is not a string" }
  }
}

Install with Tessl CLI

npx tessl i tessl/npm-ts-interface-checker

docs

checker-creation.md

data-validation.md

error-handling.md

index.md

interface-method-validation.md

type-definition-system.md

tile.json