ElysiaJS: The Type System — How Perfect Type Safety Actually Works
This is the tricky part. The thing that makes ElysiaJS special.
Forgive me, Salty, but when I document this, someone might finally understand the magic behind how you have perfect type safety.
So let's dive into it.
How does Elysia create the TypeScript server for your autocomplete? Wow. Let's do this.
The First Piece: format.ts
First, there's format.ts. This file is quite interesting and surprising — without the TypeScript TypeBox update, it uses regex. Well-written regex.
Link: elysiajs/elysia/blob/main/src/type-system/format.ts
Aight. This file is a banger.
What This File Is
The comment at the top says it all:
/**
* ? Fork of ajv-formats without ajv as dependencies
* @see https://github.com/ajv-validator/ajv-formats
**/ajv-formats is a plugin for AJV (the most popular JSON schema validator) that adds format validation — email, uuid, date, etc.
Elysia uses TypeBox, not AJV.
So SaltyAOM took the format validators from ajv-formats, ripped out the AJV dependency, and wired them directly into TypeBox's FormatRegistry.
Zero extra dependencies. Same functionality.
Structure — Three Layers
Layer 1: The Raw Validators
Pure functions and regexes. No TypeBox involved yet.
Date Validation — Not Just Regex
const DATE = /^(\d\d\d\d)-(\d\d)-(\d\d)$/
const DAYS = [0, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31]
function date(str: string): boolean {
const matches = DATE.exec(str)
if (!matches) return false
const year = +matches[1]
const month = +matches[2]
const day = +matches[3]
return (
month >= 1 &&
month <= 12 &&
day >= 1 &&
day <= (month === 2 && isLeapYear(year) ? 29 : DAYS[month])
)
}This is doing more than most people expect. It's not just testing the regex pattern — it's also checking that the day is actually valid for that month, including leap year handling for February.
| Input | Result |
|---|---|
2024-02-29 | ✅ Passes (leap year) |
2023-02-29 | ❌ Fails |
Regex alone can't do that.
Time Validation — Handles Leap Seconds
function getTime(strictTimeZone?: boolean) {
return function time(str: string): boolean {
// ...
if (hr <= 23 && min <= 59 && sec < 60) return true
// leap second
const utcMin = min - tzM * tzSign
const utcHr = hr - tzH * tzSign - (utcMin < 0 ? 1 : 0)
return (utcHr === 23 || utcHr === -1) && (utcMin === 59 || utcMin === -1) && sec < 61
}
}23:59:60Z is a valid time — it's a leap second. This validates it correctly. Most validators don't bother. This one does.
getTime returns a function — that's how fullFormats gets two entries from it:
time: getTime(true), // strict — timezone required
'iso-time': getTime(false), // loose — timezone optionalThe Byte Validator — A Subtle Bug Fixed
const BYTE = /^(?:[A-Za-z0-9+/]{4})*(?:[A-Za-z0-9+/]{2}==|[A-Za-z0-9+/]{3}=)?$/gm
function byte(str: string): boolean {
BYTE.lastIndex = 0 // ← this line is the fix
return BYTE.test(str)
}The g flag on a regex makes it stateful — lastIndex persists between calls.
Without BYTE.lastIndex = 0, calling byte() twice in a row would give wrong results on the second call. He resets it manually before every test.
Classic JS footgun, correctly handled.
The Regex Validator — Rejects \Z
const Z_ANCHOR = /[^\\]\\Z/
function regex(str: string): boolean {
if (Z_ANCHOR.test(str)) return false
try {
new RegExp(str)
return true
} catch (e) {
return false
}
}\Z is a valid anchor in Python and Ruby regex but not in JavaScript.
If someone passes a regex with \Z, it would silently do the wrong thing in JS. So he rejects it upfront. Then wraps new RegExp() in try/catch because invalid regex throws.
Clean.
Number Validators — Honest About JS Limits
function validateInt64(value: number): boolean {
// JSON and javascript max Int is 2**53, so any int that passes isInteger is valid for Int64
return Number.isInteger(value)
}
function validateNumber(): boolean {
return true // always true
}validateNumber always returns true. Not lazy — just honest.
JS number is already a C double, so any number that got this far is already a valid double. There's nothing to check.
validateInt64 uses Number.isInteger instead of the actual int64 range -(2**63) to 2**63-1 because JavaScript can't represent integers that large without BigInt. It checks what JS can actually guarantee, not the theoretical spec limit.
The comment admits it directly.
Layer 2: The fullFormats Map
export const fullFormats = {
date,
time: getTime(true),
'date-time': getDateTime(true),
'iso-time': getTime(false),
'iso-date-time': getDateTime(false),
duration: /^P(?!$)((\d+Y)?(\d+M)?(\d+D)?...$/,
uri,
email: /^[a-z0-9!#$%&'*+/=?^_`{|}~-]+.../i,
// ...
password: true, // ← just true
binary: true // ← just true
} as constpassword and binary are true. That's intentional — these are OpenAPI format hints, not validation rules.
| Format | What it means |
|---|---|
password | String to render as <input type="password"> |
binary | Signal to consumer, not a constraint |
There's nothing to validate about them.
Layer 3: Registering into TypeBox
This is where it connects to Elysia's actual validation system.
Date and Datetime Get Special Treatment
if (!FormatRegistry.Has('date'))
FormatRegistry.Set('date', (value: string) => {
const temp = parseDateTimeEmptySpace(value).replace(/"/g, '')
if (
isISO8601.test(temp) ||
isFormalDate.test(temp) ||
isShortenDate.test(temp) ||
_validateDate(temp)
) {
const date = new Date(temp)
if (!Number.isNaN(date.getTime())) return true
}
return false
})Three extra regex patterns get tried before the strict RFC3339 validator:
| Pattern | Matches |
|---|---|
isISO8601 | Standard ISO format with milliseconds and timezone |
isFormalDate | "Mon Jan 01 2024 00:00:00 GMT+0000 (UTC)" — what Date.toString() produces |
isShortenDate | "01/01/2024" or "2024-01-01" — common human formats |
Then even after a regex match, it does new Date(temp) and checks isNaN(date.getTime()).
Because regex can match 2024-13-45 and that's not a real date. Final safety net.
The parseDateTimeEmptySpace Edge Case
export const parseDateTimeEmptySpace = (str: string) => {
if (str.charCodeAt(str.length - 6) === 32) // 32 = space character
return str.slice(0, -6) + '+' + str.slice(-5)
return str
}Some databases return datetimes like "2024-01-01 00:00:00 05:30" with a space before the timezone offset instead of a +.
This converts that space to a + so the rest of the validation works. That's a very specific real-world bug someone hit in production.
The if (!FormatRegistry.Has(...)) Guard
if (!FormatRegistry.Has('date'))
FormatRegistry.Set('date', ...)Every registration is guarded. If something already registered date — another plugin, user code, whatever — Elysia doesn't clobber it.
Non-destructive registration throughout.
Everything Else Gets Bulk-Registered
Object.entries(fullFormats).forEach(([formatName, formatValue]) => {
if (!FormatRegistry.Has(formatName)) {
if (formatValue instanceof RegExp)
FormatRegistry.Set(formatName, (value) => formatValue.test(value))
else if (typeof formatValue === 'function')
FormatRegistry.Set(formatName, formatValue)
// true values (password, binary) are skipped — no validator needed
}
})| Input Type | Action |
|---|---|
RegExp | Wrap in function that calls .test() |
function | Register directly |
true | Silently skipped (nothing to register) |
Four Custom Elysia Formats (Not in Any Spec)
FormatRegistry.Set('numeric', (value) => !!value && !isNaN(+value))
FormatRegistry.Set('integer', (value) => !!value && Number.isInteger(+value))
FormatRegistry.Set('boolean', (value) => value === 'true' || value === 'false')
FormatRegistry.Set('ObjectString', (value) => { ... })
FormatRegistry.Set('ArrayString', (value) => { ... })These exist because query parameters and path params arrive as strings.
?count=5 gives you the string "5", not the number 5.
| Format | Purpose |
|---|---|
numeric | Validates string is coercible to number |
integer | Validates string is coercible to integer |
boolean | Validates string is literally "true" or "false" |
ObjectString / ArrayString | Validates string is parseable JSON object/array |
These are Elysia-specific formats for the HTTP layer that don't exist in JSON Schema spec.
ObjectString and ArrayString — The charCodeAt Trick
FormatRegistry.Set('ObjectString', (value) => {
let start = value.charCodeAt(0)
if (start === 9 || start === 10 || start === 32) // tab, newline, space
start = value.trimStart().charCodeAt(0)
if (start !== 123 && start !== 91) return false // 123 = '{', 91 = '['
try {
JSON.parse(value)
return true
} catch {
return false
}
})charCodeAt instead of startsWith — character code comparison is faster than string comparison.
123={91=[
Check the first character, bail early if it's neither, then try to parse. No unnecessary work.
Note:
ObjectStringandArrayStringhave identical implementations right now.ObjectStringshould probably reject arrays and vice versa. That's arguably a bug — or at least a TODO. But it works for the current use case: "is this a JSON string at all."
The Full Picture
This file does exactly one thing:
Tell TypeBox how to validate every format string that might appear in an Elysia schema.
- RFC3339 dates
- Emails
- UUIDs
- IPv4, IPv6
- URIs
All from ajv-formats with AJV stripped out.
Plus Elysia's own HTTP-layer formats for strings that look like numbers, booleans, and JSON objects.
| Feature | Status |
|---|---|
| No dependencies added | ✅ |
| Formats registered non-destructively | ✅ |
| Validators sorted cheapest-first | ✅ |
| Real-world edge cases handled (leap seconds, leap years, database datetime formats) | ✅ |
| Stateful regex reset | ✅ |
What's Next?
The next file is index.ts. This file uses @sinclair/typebox. I don't know why, but that lib is so troublesome with ElysiaJS internals.
I actually considered not reviewing this file. But I feel like I have to.
So let's do it next.
Type system. Index.
Nothing Blog. Part of the Nothing Ecosystem.
Built by Ernest Tech House · Kenya · 2026