typeforce - Runtime Type Checking
Typeforce is a JavaScript library designed for biased runtime type checking, offering a comprehensive suite of utilities to enforce data structures and primitive types. As of version 1.18.0, it provides a flexible API for defining complex type schemas, including support for arrays, recursive objects, optional properties (`?`), sum types (`anyOf`), and intersection types (`allOf`). A notable feature is its extensibility through custom type functions, allowing developers to define domain-specific validations. It differentiates itself by offering specialized modules for non-throwing error handling (`typeforce/nothrow`) and asynchronous validation (`typeforce/async`), catering to different error management strategies. While a specific release cadence isn't detailed, its versioning suggests ongoing maintenance and feature development, making it a robust choice for projects requiring strict data validation without relying on static type systems.
Common errors
-
TypeError: Expected Number, got Array
cause Attempting to validate a value against an incorrect primitive type.fixEnsure the type string or type object provided to `typeforce` accurately reflects the expected type of the `value` being validated. For example, `typeforce('Array', someArray)` instead of `typeforce('Number', someArray)`. -
TypeError: Unexpected property 'y' of type Number
cause Using strict mode for object validation (`typeforce(schema, value, true)`) where `value` contains properties not declared in `schema`.fixIf extra properties are allowed, remove the `true` argument for strictness. If they are not allowed, ensure your schema lists all permissible properties, using `?Type` for optional ones. -
TypeError: Expected property "0" of type Number, got String 'not a number'
cause A value in a tuple (fixed-length array) does not match the type specified for its position.fixCheck the order and types of elements in the array being validated against `typeforce.tuple()`. Each element must precisely match the corresponding type in the tuple definition. -
Oops, Expected Number, got String foobar
cause The `typeforce/nothrow` module was used, and a type validation failed, leading to the error message being stored in `typeforce.error.message`.fixThis is often expected behavior for `typeforce/nothrow`. The fix involves checking the return value of the `typeforceNoThrow` call; if it's `false`, then `typeforceNoThrow.error.message` will contain the validation failure reason.
Warnings
- gotcha The `quacksLike` type relies on the `Function.name` property, which can be mangled by transpilers like UglifyJS. This can lead to unexpected type validation failures in minified production builds.
- gotcha Exception messages may change between patch versions as underlying behaviors are refined. Relying on the exact text of error messages in tests or logic is fragile.
- gotcha When using typeforce with strictness (`typeforce(schema, value, true)`), any properties in the `value` that are not explicitly defined in the `schema` will cause a `TypeError` to be thrown. This is intended behavior for whitelisting properties.
Install
-
npm install typeforce -
yarn add typeforce -
pnpm add typeforce
Imports
- typeforce
import typeforce from 'typeforce'
const typeforce = require('typeforce') - typeforce.Number
import { Number } from 'typeforce'const { Number } = require('typeforce') - typeforce/nothrow
import { nothrow } from 'typeforce'const typeforceNoThrow = require('typeforce/nothrow')
Quickstart
var typeforce = require('typeforce');
var typeforceNoThrow = require('typeforce/nothrow');
// Define some data
var user = { id: 123, name: 'Alice' };
var product = { id: 456, price: 10.99 };
var inventory = [user, product];
var configTuple = ['development', 8080];
// Basic type checking for primitives
typeforce('Array', inventory); // OK
typeforce('Number', 123); // OK
// Array of a specific type (e.g., objects with an ID and a string name)
typeforce(typeforce.arrayOf({ id: 'Number', name: 'String' }), [user]);
// Recursive type templating for objects with optional properties
typeforce({ id: 'Number', name: '?String', price: '?Number' }, product);
// Sum types (anyOf) and intersection types (allOf)
typeforce(typeforce.anyOf('String', 'Number'), 'foobar'); // 'foobar' is String
typeforce(typeforce.allOf({ x: typeforce.Number }, { y: typeforce.String }), { x: 1, y: '2' }); // Both properties must match
// Custom type definition (e.g., a specific length string)
function HexString32(value) {
return typeforce.String(value) && /^[0-9a-fA-F]{32}$/.test(value);
}
typeforce(HexString32, 'a0b1c2d3e4f5a0b1c2d3e4f5a0b1c2d3'); // OK!
// Using the non-throwing version for graceful error handling
var potentiallyBadValue = 'this is not a number';
if (!typeforceNoThrow(typeforceNoThrow.Number, potentiallyBadValue)) {
console.log(`Error caught by no-throw: ${typeforceNoThrow.error.message}`);
}
// Protip: use precompiled types for performance on repeated checks
var compiledSchema = typeforce.compile({
id: typeforce.Number,
timestamp: typeforce.Number
});
compiledSchema({ id: 1, timestamp: Date.now() }); // Fast check!
// Protip: enforce strictness to disallow extra properties
typeforce({ a: 'Number' }, { a: 1 }, true); // OK
try {
typeforce({ a: 'Number' }, { a: 1, b: 2 }, true); // This will throw an error
} catch (e) {
console.log(`Strict check error: ${e.message}`);
}