runtimekit
v1.0.5
Published
Composable runtime validation for TypeScript — define schemas once, infer static types automatically.
Downloads
545
Maintainers
Readme
runtimekit
Composable runtime validation for TypeScript.
runtimekit lets you define schemas as values, validate untrusted data at runtime, and derive static TypeScript types from those same definitions — no duplication required.
Install
npm install runtimekitRequires Node.js 18 or later. Works with both ESM (import) and CommonJS (require).
Quick start
Define a schema, parse unknown input, and get a typed result:
import { Object, String, Number, Literal, Array, Static } from 'runtimekit';
const User = Object({
id: Number,
name: String,
role: Literal('admin', 'member', 'guest'),
});
type User = Static<typeof User>;
const user = User.parse({ id: 1, name: 'Ada', role: 'admin' });
// ^? UserIf validation fails, parse throws a ValidationError with a readable message.
Core API
| Method | Behavior |
|--------|----------|
| parse(value) | Validate and return the typed value, or throw |
| safeParse(value) | Return { success: true, value } or { success: false, error } |
| test(value) | Type guard — returns true when the value matches |
| assert(value) | Like parse, but accepts unknown and narrows the type |
| serialize(value) | Validate and serialize to a plain JSON-safe value |
Building schemas
Schemas compose from primitives and combinators:
import {
Boolean,
Number,
String,
Literal,
Union,
Array,
Tuple,
Object,
Intersect,
Partial,
Constraint,
} from 'runtimekit';
const Vector = Tuple(Number, Number, Number);
const Planet = Object({
type: Literal('planet'),
location: Vector,
mass: Number,
population: Number,
habitable: Boolean,
});
const Positive = Constraint(Number, n => n > 0 || `${n} is not positive`);
const Fleet = Object({
name: String,
ships: Array(
Object({
name: String,
crew: Number,
position: Vector,
}),
),
});Unions and intersections
const Rank = Union(
Literal('captain'),
Literal('officer'),
Literal('ensign'),
);
const RegisteredShip = Intersect(
Fleet,
Object({ isRegistered: Literal(true) }),
Partial({ classification: Union(Literal('military'), Literal('civilian')) }),
);Optional and nullable fields
import { Union, Undefined, Null, Nullable } from 'runtimekit';
const MaybeString = Union(String, Undefined); // string | undefined
const NullableString = Nullable(String); // string | null
const Profile = Object({ bio: String }).And(
Partial({ website: Union(String, Undefined) }),
);Type inference
Use Static<typeof Schema> to derive a TypeScript type from any schema:
import { Static } from 'runtimekit';
type Planet = Static<typeof Planet>;
// { type: 'planet'; location: [number, number, number]; mass: number; ... }Type guards
Use schemas as runtime type guards:
function handle(input: unknown) {
if (Planet.test(input)) {
// input is narrowed to Planet
console.log(input.population);
}
}Constraints and custom validators
Add arbitrary runtime checks with Constraint:
const Email = Constraint<string, `${string}@${string}`>(
String,
value => value.includes('@') || 'Invalid email address',
{ name: 'Email' },
);For custom types backed by a type guard:
import { Guard } from 'runtimekit';
const BufferSchema = Guard(Buffer.isBuffer, { name: 'Buffer' });Readonly schemas
Wrap any schema with Readonly to produce immutable inferred types:
import { Readonly, ReadonlyArray } from 'runtimekit';
const ImmutablePlanet = Readonly(Planet);
type ImmutablePlanet = Static<typeof ImmutablePlanet>;
// { readonly type: 'planet'; readonly location: readonly [number, number, number]; ... }Import from runtimekit/readonly to default all collection helpers to their readonly variants:
import { Object, Array, Tuple } from 'runtimekit/readonly';
// Object, Array, and Tuple produce readonly types by defaultObject utilities
Mirror TypeScript's built-in object utilities:
import { Partial, Pick, Omit } from 'runtimekit';
const PartialPlanet = Partial(Planet);
const PlanetMass = Pick(Planet, ['mass']);
const PlanetWithoutMass = Omit(Planet, ['mass']);Safe parsing
Prefer non-throwing validation at API boundaries:
const result = User.safeParse(untrusted);
if (result.success) {
console.log(result.value.name);
} else {
console.error(result.error);
}License
MIT © yuliahrabets
