Open Source · Zero-Alloc · Standard Schema

Validate anything.
Allocate nothing.

A TypeScript validation library. Chain readable constraints onto a type; TypKit compiles it once into a single monomorphic function. The happy path allocates nothing, and rich constraints compile inline — the rules other validators can't. Node 18+, Bun, and the browser.

quick-start.ts
import { t } from '@myrialabs/typkit';

const Comment = t.object({
  author: t.string().minLength(3).charset('a-z0-9_'),
  email:  t.string().email(),
  body:   t.string().nonEmpty().maxLines(500).blockWords(BANNED),
  rating: t.int().min(1).max(5),
});

Comment.check(value);      // → boolean (zero-allocation)
type Comment = t.infer<typeof Comment>;

Everything a validator needs,
compiled to one function

Declarative config-object schemas, compiled once into a single monomorphic validator — fast, complete, and interoperable.

Rich Constraints, Inline
maxLines, charset, blockWords, formats, ranges — all compile to literal loops. Competitors fall back to closures (2.3–3.2× slower); TypeBox can't express them at all.
Zero-Allocation Hot Path
check() returns a boolean — a validated value is never wrapped, and an issue path is built only on failure. It sits in the compiled fast tier with ArkType and TypeBox.
Standard Schema
Implements ~standard, so Elysia, Hono, tRPC, react-hook-form, and TanStack Form accept a schema directly — no adapter, no glue code.
JSON Schema Export
toJsonSchema() maps 1:1 from the schema, so frameworks generate OpenAPI / Swagger straight from your validation schema.
Full Type Inference
t.infer<typeof S> is precise — optional keys become ?, unions and discriminated unions infer exactly — and stays light on tsc.
Discriminated Unions, O(1)
discriminatedUnion dispatches on its tag through a switch — flat cost regardless of member count. The hot path for streaming, tagged messages.
Self-Documenting API
A readable fluent chain — t.int().min(1).max(5) — with no cryptic gte/lt: min/max are inclusive, greaterThan/lessThan exclusive. Chaining is build-time only, so it costs nothing at runtime.
Query Coercion
t.coerce.number / boolean / date turn string params into typed values for query and path inputs — check stays boolean, parse returns the coerced value.
Framework Ready
Native with Elysia; a tiny Hono validator and tRPC input parser ship in @myrialabs/typkit/middleware. Body, query, params, response.
Recursive Schemas
t.lazy defers construction for self-referential trees (categories, comment threads, ASTs) and compiles them into memoized helper functions that terminate.
Cross-Runtime, Zero-Dep
The same import runs on Node 18+, Bun, and the browser, with no runtime dependency. The Standard Schema type is vendored, not depended on.
Complete Toolkit
Objects with partial/pick/omit/extend/strict, arrays, tuples, records, unions, intersections, optional, nullable, default, enums, bigint, dates, and a dozen formats.

One schema definition,
every output you need

Rich constraints that stay fast

The differentiator: rules like maxLines, charset, and blockWords compile to inline loops instead of closures. The same schema that reads cleanly runs 2–4× faster than a refinement chain.

  • Content rules: maxLines, blockWords, allowedWords
  • A dozen formats: email, uuid, ipv4, creditCard (Luhn)
  • Inclusive min/max, exclusive greaterThan/lessThan
  • Issue paths built only when validation fails
constraints.ts
const Post = t.object({
  slug:  t.string().slug(),
  body:  t.string().nonEmpty().maxLines(500),
  price: t.number().greaterThan(0),
  card:  t.string().format('creditCard'),
});

const r = Post.parse(input, { abortEarly: false });
if (r.issues) r.issues.forEach((i) =>
  console.log(i.path.join('/'), i.message));

Drops into your framework

Every schema is a Standard Schema, so Elysia validates body, query, and params from a TypKit schema with no adapter — valid requests reach the handler, invalid ones return 422 with the issue path.

  • Elysia: pass the schema straight into the route
  • Query t.coerce.* decodes string params automatically
  • Hono: validator(target, schema) from @myrialabs/typkit/middleware
  • tRPC v11: a schema is an input parser as-is
server.ts
import { Elysia } from 'elysia';
import { t } from '@myrialabs/typkit';

new Elysia()
  .post('/users', ({ body }) => body, {
    body: t.object({
      name: t.string().minLength(2),
      age: t.int().min(0),
    }),
  })
  .listen(3000);

JSON Schema in, OpenAPI out

The config-object API maps almost 1:1 onto JSON Schema, so toJsonSchema() is a direct export. Feed it to any OpenAPI generator — or let Elysia's plugin read it straight from the route.

  • Objects, arrays, enums, unions, defaults, formats
  • Exclusive bounds → exclusiveMinimum/Maximum
  • Discriminated unions → oneOf
  • Available on every node, including nested ones
openapi.ts
const User = t.object({
  id:   t.int().min(1),
  name: t.string().minLength(2),
  role: t.enum(['admin', 'user']).default('user'),
});

User.toJsonSchema();
// { type: 'object', properties: {…},
//   required: ['id', 'name'] }

Discriminated unions, dispatched in O(1)

Tagged messages are the streaming hot path. TypKit reads the discriminant once and jumps through a switch — the cost is flat no matter how many members you add.

  • One switch on the tag, no sequential member tries
  • The matched member skips its redundant re-checks
  • Inferred as a precise discriminated union type
  • Exports to oneOf for OpenAPI
events.ts
const Event = t.discriminatedUnion('type', [
  t.object({ type: t.literal('click'), x: t.number(), y: t.number() }),
  t.object({ type: t.literal('key'), code: t.string() }),
]);

Event.check({ type: 'click', x: 10, y: 20 }); // true

Install & start validating

Requires Node.js v18+ or Bun. Zero runtime dependencies — one package, no build step.

terminal
01
Add @myrialabs/typkit to your project
$ npm install @myrialabs/typkit
02
Build a schema and validate
t.object({ name: t.string().minLength(2) }).check(value)

Using Bun? bun add @myrialabs/typkit · then import { t } from '@myrialabs/typkit'

Declarative schema,
JIT-compiled validator

A schema tree is compiled — once, on first use — into a single monomorphic function via new Function. No build step, no transformer, no Wasm.

new Function JIT Zero-alloc hot path Inline constraints Standard Schema JSON Schema TypeScript Node.js 18+ Bun Browser Elysia Hono tRPC

Ready to validate
everything?

Rich inline constraints, a zero-allocation hot path, Standard Schema, and JSON Schema — in one typed API that runs the same on Node, Bun, and the browser.