Typanion
Typanion is a lean, type-safe runtime TypeScript validator library with zero external runtime dependencies. It excels at validating complex, nested data structures and provides strong type inference, which allows TypeScript to refine types based on successful validation. Unlike some alternatives, Typanion emphasizes a functional and tree-shakeable API, making it efficient for bundlers. It provides detailed error reports and supports coercions, enabling data transformation during validation. While it may not have the extensive ecosystem of libraries like Zod or Yup, its core differentiators lie in its minimal footprint, functional design, and robust TypeScript inference. Currently, in version 3.14.0, its release cadence appears less frequent, suggesting a focus on stability over rapid iteration, with the last major activity around two years ago.
Common errors
-
Validation failed for path: <path>. Expected <type>, received <other_type>.
cause An input value did not match the expected type or structure defined by the schema.fixInspect the `errors` array returned by the validation function for detailed messages. Adjust the input data to conform to the schema or refine the schema to accurately reflect the expected data shape. For example, if 'Expected number, received string' ensure the field is parsed as a number before validation. -
TypeError: (0 , typanion_1.isObject) is not a function
cause Incorrect import of predicate functions, often due to mixing CommonJS `require` with ESM named imports or attempting to destructure `t` itself.fixEnsure you are using `import * as t from 'typanion'` and accessing predicates as `t.isObject` or explicitly named imports `import { isObject } from 'typanion'`. Avoid `const { isObject } = require('typanion')` if the package is primarily ESM. -
Argument of type 'string' is not assignable to parameter of type 'number'.
cause Attempting to assign a value that failed a `typanion` predicate (e.g., `t.isNumber()`) to a TypeScript type that expects the validated type.fixWrap the code that uses the validated value within the `if (validator(value))` block. `typanion` uses type predicates, so TypeScript's type narrowing only applies inside the conditional block where validation is successful. If coercion is used, ensure `coercions` are flushed.
Warnings
- gotcha When validating objects, `typanion` is strict by default and will report errors for extraneous properties not defined in the schema. This differs from some other validators that might ignore unknown properties.
- gotcha The `isDate()` predicate has known limitations regarding ISO8601 string parsing and does not natively support milliseconds, which can lead to unexpected validation failures for certain date formats.
- gotcha When using `t.cascade()` with `isHexColor`, the predicate may not be supported directly, leading to validation issues for hex color strings.
- gotcha Unexpected type widening can occur when using nested `isEnum` predicates, potentially leading to less precise type inference than expected in complex schemas.
Install
-
npm install typanion -
yarn add typanion -
pnpm add typanion
Imports
- t
const t = require('typanion')import * as t from 'typanion'
- isString, isNumber, isObject
import { t.isString } from 'typanion'import { isString, isNumber, isObject } from 'typanion' - Coercion
type Coercion = any[]
import { Coercion } from 'typanion'
Quickstart
import * as t from 'typanion';
// Define a schema for a user object
const isUser = t.isObject({
id: t.isNumber(),
name: t.isString(),
email: t.isOptional(t.isString()),
age: t.cascade(t.isNumber(), [t.isInteger(), t.isInInclusiveRange(0, 120)]),
roles: t.isArray(t.isString(), { maxLength: 3 }),
isActive: t.isBoolean(),
});
// Example valid data
const validUserData = {
id: 123,
name: 'Alice',
email: 'alice@example.com',
age: 30,
roles: ['admin', 'editor'],
isActive: true,
};
// Example invalid data
const invalidUserData = {
id: 'abc', // Should be number
name: 123, // Should be string
age: 150, // Out of range
roles: ['guest', 'viewer', 'reporter', 'qa'], // Too many roles
unknownProp: 'oops' // Extraneous property (fails by default)
};
interface User {
id: number;
name: string;
email?: string;
age: number;
roles: string[];
isActive: boolean;
}
// Validate valid data
const errorsForValid: string[] = [];
if (isUser(validUserData, { errors: errorsForValid })) {
console.log('Valid user data:', validUserData);
// TypeScript knows validUserData is now of type User
const user: User = validUserData;
console.log(user.name);
} else {
console.error('Validation failed for valid data:', errorsForValid);
}
console.log('\n--- Attempting to validate invalid data ---');
// Validate invalid data
const errorsForInvalid: string[] = [];
if (isUser(invalidUserData, { errors: errorsForInvalid })) {
console.log('Invalid user data (unexpected success):', invalidUserData);
} else {
console.error('Validation failed for invalid data:');
errorsForInvalid.forEach(error => console.error(`- ${error}`));
// TypeScript still considers invalidUserData as 'unknown' here
// because validation failed.
}
// Example with coercion (though not typical for this schema)
const isCoercibleNumber = t.applyCoercion(t.isNumber(), t.isString());
const coercions: t.Coercion[] = [];
const potentiallyNumber = '42';
if (isCoercibleNumber(potentiallyNumber, { coercions })) {
for (const [p, op] of coercions) op();
console.log('\nCoerced value:', potentiallyNumber); // will be 42 (number)
const num: number = potentiallyNumber;
console.log(typeof num); // 'number'
} else {
console.error('Coercion failed');
}