Skip to content

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:

ts
/**
 * ? 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

ts
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.

InputResult
2024-02-29✅ Passes (leap year)
2023-02-29❌ Fails

Regex alone can't do that.


Time Validation — Handles Leap Seconds

ts
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:

ts
time: getTime(true),        // strict — timezone required
'iso-time': getTime(false), // loose — timezone optional

The Byte Validator — A Subtle Bug Fixed

ts
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 statefullastIndex 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

ts
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

ts
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

ts
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 const

password and binary are true. That's intentional — these are OpenAPI format hints, not validation rules.

FormatWhat it means
passwordString to render as <input type="password">
binarySignal 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

ts
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:

PatternMatches
isISO8601Standard 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
ts
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
ts
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
ts
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 TypeAction
RegExpWrap in function that calls .test()
functionRegister directly
trueSilently skipped (nothing to register)

Four Custom Elysia Formats (Not in Any Spec)
ts
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.

FormatPurpose
numericValidates string is coercible to number
integerValidates string is coercible to integer
booleanValidates string is literally "true" or "false"
ObjectString / ArrayStringValidates 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
ts
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 startsWithcharacter 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: ObjectString and ArrayString have identical implementations right now. ObjectString should 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.

FeatureStatus
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

MIT Licensed | Built by Ernest Tech House · Kenya · 2026