CtrlK
BlogDocsLog inGet started
Tessl Logo

tessl/npm-io-ts

TypeScript runtime type system for IO decoding/encoding

72

1.14x
Overview
Eval results
Files

reporters.mddocs/

Error Reporting System

Error reporting interfaces and implementations for converting validation failures into human-readable messages.

Capabilities

Reporter Interface

Base interface for error reporting implementations.

/**
 * Generic interface for converting validation results to custom output formats
 * @template A - Output type for the reporter
 */
interface Reporter<A> {
  /** Function that processes validation results and returns formatted output */
  report: (validation: Validation<any>) => A;
}

PathReporter

The main error reporter that converts validation errors to readable string messages with path information.

/**
 * Reporter that converts validation errors to an array of error messages
 */
const PathReporter: Reporter<Array<string>>;

/**
 * Convert validation errors to string messages
 * @param es - Array of validation errors
 * @returns Array of formatted error messages
 */
function failure(es: Array<ValidationError>): Array<string>;

/**
 * Generate success message
 * @returns Array containing success message
 */
function success(): Array<string>;

Usage Examples:

import * as t from "io-ts";
import { PathReporter } from "io-ts/PathReporter";

const User = t.type({
  name: t.string,
  age: t.number
});

// Valid data
const validResult = User.decode({ name: "Alice", age: 30 });
console.log(PathReporter.report(validResult));
// Output: ["No errors!"]

// Invalid data
const invalidResult = User.decode({ name: 42, age: "thirty" });
console.log(PathReporter.report(invalidResult));
// Output: [
//   "Invalid value 42 supplied to name: string",
//   "Invalid value \"thirty\" supplied to age: number"
// ]

// Complex nested validation
const Address = t.type({
  street: t.string,
  city: t.string,
  zipCode: t.string
});

const Person = t.type({
  name: t.string,
  address: Address
});

const result = Person.decode({
  name: "John",
  address: { street: 123, city: "NYC" } // missing zipCode, invalid street
});

console.log(PathReporter.report(result));
// Output: [
//   "Invalid value 123 supplied to address/street: string",
//   "Invalid value undefined supplied to address/zipCode: string"
// ]

ThrowReporter (Deprecated)

⚠️ DEPRECATED: Use PathReporter instead for better error handling.

/**
 * @deprecated Use PathReporter instead
 * Reporter that throws errors instead of returning them
 */
const ThrowReporter: Reporter<void>;

Migration Example:

// ❌ Don't use ThrowReporter (deprecated)
import { ThrowReporter } from "io-ts/ThrowReporter";

try {
  ThrowReporter.report(result);
} catch (error) {
  console.error(error.message);
}

// ✅ Use PathReporter instead
import { PathReporter } from "io-ts/PathReporter";
import { isLeft } from "fp-ts/Either";

if (isLeft(result)) {
  const errors = PathReporter.report(result);
  console.error(errors.join('\n'));
}

Advanced Usage

Custom Reporter Implementation

import * as t from "io-ts";
import { fold } from "fp-ts/Either";
import { Reporter } from "io-ts/Reporter";

// Custom reporter that returns structured error objects
interface StructuredError {
  field: string;
  value: unknown;
  expected: string;
  message: string;
}

const StructuredReporter: Reporter<StructuredError[]> = {
  report: fold(
    (errors) => errors.map(error => ({
      field: error.context.map(c => c.key).filter(Boolean).join('.'),
      value: error.value,
      expected: error.context[error.context.length - 1]?.type.name || 'unknown',
      message: error.message || `Expected ${error.context[error.context.length - 1]?.type.name}`
    })),
    () => []
  )
};

// Usage
const result = t.string.decode(42);
const structuredErrors = StructuredReporter.report(result);
console.log(structuredErrors);
// Output: [{
//   field: "",
//   value: 42,
//   expected: "string",
//   message: "Expected string"
// }]

JSON Reporter

import { fold } from "fp-ts/Either";
import { Reporter } from "io-ts/Reporter";

const JSONReporter: Reporter<string> = {
  report: fold(
    (errors) => JSON.stringify({
      success: false,
      errors: errors.map(e => ({
        path: e.context.map(c => c.key).filter(Boolean).join('.'),
        value: e.value,
        message: e.message || `Invalid value supplied`
      }))
    }, null, 2),
    (value) => JSON.stringify({ success: true, data: value }, null, 2)
  )
};

Integration with Logging

import * as t from "io-ts";
import { PathReporter } from "io-ts/PathReporter";
import { pipe } from "fp-ts/function";
import { fold } from "fp-ts/Either";

const validateAndLog = <A>(codec: t.Type<A>, data: unknown): A | null => {
  return pipe(
    codec.decode(data),
    fold(
      (errors) => {
        const errorMessages = PathReporter.report(codec.decode(data));
        console.error('Validation failed:', errorMessages);
        return null;
      },
      (valid) => {
        console.log('Validation successful');
        return valid;
      }
    )
  );
};

Error Message Format

PathReporter generates error messages in the format:

  • Path-based errors: "Invalid value <value> supplied to <path>"
  • Custom message errors: Uses the custom message if provided
  • Complex paths: Nested paths shown as parent/child/grandchild

Path Examples:

  • Root level: "Invalid value 42 supplied to : string"
  • Object property: "Invalid value null supplied to name: string"
  • Nested object: "Invalid value 123 supplied to address/street: string"
  • Array element: "Invalid value "invalid" supplied to items/0: number"
  • Union types: Shows attempted matches with context

Install with Tessl CLI

npx tessl i tessl/npm-io-ts

docs

codec.md

combinators.md

core-types.md

decoder.md

encoder.md

index.md

infrastructure.md

primitives.md

refinement.md

reporters.md

schema.md

task-decoder.md

validation.md

tile.json