Typanion
repository·main·Indexed 42 days ago
https://github.com/arcanis/typanionA zero-dependency runtime TypeScript validator library for static and runtime type assertion. It provides a functional API to define schemas, validate data structures, and automatically derive TypeScript types using t.InferType. Features include type predicates, data coercion via t.as, detailed validation error reporting, and cascading predicates for complex constraints like range checks, string formats (ISO 8601, UUID 4), and object key relationships.
What's inside Typanion
- Typanion is a runtime validation library designed to integrate deeply with TypeScript's type system. It allows you to describe data structures using composable operators. From these descriptions, Typanion automatically derives both validation protocols (to check data at runtime) and type hints (to provide static type safety during development).
How cascading predicates work in Typanion
mainCascading predicates are used to assert that a value follows a specific pattern or constraint without contributing to the refinement of the value's type. Because they do not change the inferred type, they are primarily used for validation checks. To use them effectively, you should compose them using the
cascademethod.For example, if you want to validate that a value is a string and also matches a specific pattern, you would use a type-refining predicate followed by a cascading predicate via
cascade.Compose nested schemas
mainSchemas can be composed by nesting them within other schemas. For example, you can use
t.isArray(otherSchema)to validate an array of objects defined by another schema.import * as t from 'typanion'; const isActor = t.isObject({ name: t.isString(); }); const isMovie = t.isObject({ title: t.isString(), description: t.isString(), actors: t.isArray(isActor), });Validate unknown data and infer types
mainUse a Typanion validator to check
unknowndata. If the data matches the schema, Typanion provides a type refinement. You can also uset.InferTypeto extract the TypeScript type from the validator to ensure your application code stays in sync with your runtime schemas.import * as t from 'typanion'; const isBlogPost = t.isObject({ title: t.isString(), description: t.isString(), published: t.isBoolean(), }); declare const userData: unknown; // 1. Runtime validation with type refinement if (isBlogPost(userData)) { // userData is now typed as the inferred BlogPost type console.log(userData.title); } // 2. Type inference for use elsewhere in your app type BlogPost = t.InferType<typeof isBlogPost>;import * as t from 'typanion'; const isBlogPost = t.isObject({ title: t.isString(), description: t.isString(), published: t.isBoolean(), }); declare const userData: unknown; if (isBlogPost(userData)) { console.log(userData.title); } type BlogPost = t.InferType<typeof isBlogPost>;Install Typanion via Yarn
mainAdd Typanion to your project using the Yarn package manager:
yarn add typanionDefine data structures with Typanion operators
mainYou can compose prebuilt Typanion operators to describe complex data structures. These operators create a validator function that can be used to check
unknowndata at runtime.import * as t from 'typanion'; const isBlogPost = t.isObject({ title: t.isString(), description: t.isString(), published: t.isBoolean(), });Define and use schemas for type assertion
mainTypanion allows you to define schemas using built-in operators (like
t.isObject,t.isString, etc.). Once a schema is defined, you can call it as a function to validateunknowndata. Because Typanion uses TypeScript type predicates, successful validation automatically narrows the type within the conditional block.import * as t from 'typanion'; const isMovie = t.isObject({ title: t.isString(), description: t.isString(), }); const userData = JSON.parse(input); if (isMovie(userData)) { // userData is now narrowed to the inferred Movie type console.log(userData.title); }Use t.cascade for complex validation logic
mainThe
t.cascadefunction allows you to chain multiple validation steps. It first checks a primary predicate (the first argument). If that predicate returnstrue, it then executes the subsequent predicates provided in an array. This is useful for building complex validation rules where subsequent checks depend on the success of the first, such as ensuring a value is a number before checking if it falls within a specific integer range.import * as t from 'typanion'; const isPort = t.cascade(t.isNumber(), [ t.isInteger(), t.isInInclusiveRange(1, 65535), ]); // If isPort(value) returns true, the value is a number, an integer, // and within the range 1-65535.Validate an object with specific fields and unknown extra fields
mainUse
t.isObjectto validate that an object contains specific keys with specific types. To allow the object to contain other fields not defined in your schema, use theextraoption. Settingextra: t.isUnknown()allows any additional fields to exist regardless of their type.const isDiv = t.isObject({ tagName: t.isLiteral(`DIV"), }, { extra: t.isUnknown(), }); isDiv({tagName: `div`, appendChild: () => {}});Validate an object with specific fields and a dictionary of typed extra fields
mainUse
t.isObjectcombined witht.isDictto validate an object that has a known set of keys, while ensuring all other (extra) keys follow a specific type pattern. For example, you can require auidstring and ensure all other properties in the object are numbers.const isModel = t.isObject({ uid: t.isString(), }, { extra: t.isDict(t.isNumber()), }); isModel({uid: `foo`, valA: 12, valB: 24});Validate a value using multiple constraints with applyCascade
mainUse
t.applyCascadeto chain multiple validation predicates together. The first predicate is the base validator, and the subsequent predicates in the array are applied sequentially. If any predicate in the cascade fails, the entire validation fails. This is useful for complex logic like checking if a number is both an integer and within a specific range (e.g., a port number).const isPort = t.applyCascade(t.isNumber(), [ t.isInteger(), t.isInInclusiveRange(1, 65535), ]); isPort(42000);Validate numbers with `isNumber`
mainUse
isNumberto ensure values are numbers. This predicate supports coercion.const validate = t.isNumber();