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.
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.
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.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, so Elysia, Hono, tRPC, react-hook-form, and TanStack Form accept a schema directly — no adapter, no glue code.toJsonSchema() maps 1:1 from the schema, so frameworks generate OpenAPI / Swagger straight from your validation schema.t.infer<typeof S> is precise — optional keys become ?, unions and discriminated unions infer exactly — and stays light on tsc.discriminatedUnion dispatches on its tag through a switch — flat cost regardless of member count. The hot path for streaming, tagged messages.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.t.coerce.number / boolean / date turn string params into typed values for query and path inputs — check stays boolean, parse returns the coerced value.@myrialabs/typkit/middleware. Body, query, params, response.t.lazy defers construction for self-referential trees (categories, comment threads, ASTs) and compiles them into memoized helper functions that terminate.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, exclusivegreaterThan/lessThan - ✓ Issue paths built only when validation fails
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
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
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
switchon the tag, no sequential member tries - ✓ The matched member skips its redundant re-checks
- ✓ Inferred as a precise discriminated union type
- ✓ Exports to
oneOffor OpenAPI
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.
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.
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.