Guide

Linting

See RunTypes problems in your editor and block dirty enrichment files at commit time.

RunTypes ships a lint plugin inside the @ts-runtypes/devtools package. It works with OXlint (the primary target) and with ESLint v9 flat config. The same compiler that powers your build produces the findings, so what you see in the editor is exactly what the build would say.

Rules

RuleDefaultWhat it reports
runtypes/broken-tsconfigerrorThe tsconfig the linter was pointed at (your tsconfig setting, or the default tsconfig.json) is missing or does not parse, so type-aware linting cannot run. The linter reads the same config as your build, so fix the config or the configured path.
runtypes/invalid-markererrorA marker call the build cannot turn into a function: a generic type argument that is never filled in with a concrete type, an options argument that is not a plain literal, an import that failed to resolve, or a Temporal type without the Temporal lib enabled.
runtypes/redundant-markerwarnA marker that works but probably does not do what you meant, such as calling a function inside a marker just to read its return type (the call itself is wasted), or an option that has no effect on this particular type.
runtypes/pure-functionserrorA registered pure function that breaks the purity rules (uses this, await, or variables from outside its own body), is registered twice with different bodies, or is referenced but never registered. These functions are compiled at build time, so they must be fully self contained.
runtypes/validate-non-serializableerrorA type that can never be validated. Validators check the data-only projection of your type (the JSON-shaped data, the same contract the encoders follow), so a type like symbol or Map at a root position has nothing to check, and the generated function will always fail when called.
runtypes/validate-skipped-memberwarnA property the validator silently skips. Functions, methods, statics, and symbols are not data and never survive JSON, so the validator checks the rest of the object and ignores them.
runtypes/json-non-serializableerrorA type that can never be encoded to or decoded from JSON, such as a function, a symbol, or a Map at a root position. The generated function will always fail when called, so the build stops instead.
runtypes/json-skipped-memberwarnA property the JSON encoder and decoder silently leave out, such as a function or symbol member. The rest of the object round-trips normally.
runtypes/binary-non-serializableerrorA type that can never be serialized to or from binary, such as a function, a symbol, or a Map at a root position. The generated function will always fail when called, so the build stops instead.
runtypes/binary-skipped-memberwarnA property the binary encoder and decoder silently leave out, such as a function or symbol member. The rest of the object round-trips normally.
runtypes/clone-unsupported-typeerrorA type cloneExactShape cannot clone safely: a union of objects (the clone cannot tell which shape to rebuild) or a callable root. A clone that guessed could keep unknown keys, so the build stops instead.
runtypes/clone-shared-referencewarnA property the clone cannot rebuild (a function, symbol, or non-serializable built-in), so it stays pointing at the same value as the original. Changes through it are visible on both copies.
runtypes/unknown-keyswarnA property the unknown-keys helpers (hasUnknownKeys and friends) silently skip, such as a function member. The check covers the rest of the object.
runtypes/formaterrorA custom string format with a broken definition: a mock sample that does not match its own pattern, a sample that violates a sibling rule like maxLength, or invalid params. Also fires when the linter re-checks samples the build could not verify (see allowUncheckedPatterns).
runtypes/invalid-overrideerrorAn override that cannot work: the same type and function registered twice, or an override pointing at a generated module that does not exist.
runtypes/override-side-effectwarnA validate override on a type whose JSON and binary decoders also run validation internally. The override changes their behaviour too, which may be intended, but is worth knowing.
runtypes/non-enumerableerrorA property marked @nonEnumerable that is not optional. A non-enumerable property can be absent from a plain object, so the type must allow undefined.
runtypes/class-serializerwarnA class that will be serialized structurally (declared properties only) because no custom serializer is registered. The data survives, but the decoded value is a plain object, not a class instance. Register one to get real instances back.
runtypes/no-enrichment-todoerrorAn unfilled @todo placeholder the generator scaffolded in a FriendlyText or MockData file. Fill in the value, then delete the tag line.
runtypes/no-orphan-carcasserrorA commented-out @rtOrphan or @rtOrphanChild block the generator left behind when a type or field disappeared. Restore the type, or run ts-runtypes enrich --prune to remove it.
runtypes/enrichment-fielderrorAn entry in a FriendlyText or MockData map that no longer matches the type: a field the type does not declare, or a name colliding with the reserved rt$ prefix.
runtypes/enrichment-messagewarnA friendly error message template with a problem: an error key that is not a constraint of the field, an unknown placeholder, or a plural form that is not a valid category.
runtypes/enrichment-broken-sourceerrorA generated file whose source is gone: the file it mirrors no longer exists, or no longer declares the imported type. Re-run the generator, or delete the mirror.
runtypes/enrichment-misplaced-filewarnA generated file that is no longer where the generator would write it, usually after its source file moved. Re-run the generator to relocate it.

Setup

There is no separate package to install. Extend the recommended setup that ships inside @ts-runtypes/devtools and every rule is enabled at the defaults above. To change a rule's level, set it in your own config (your entries win over the preset).

{
  "$schema": "./node_modules/oxlint/configuration_schema.json",
  "extends": ["./node_modules/@ts-runtypes/devtools/oxlint-recommended.json"],
  "rules": {
    "runtypes/validate-skipped-member": "off"
  }
}

Your tsconfig

The plugin reads your full project tsconfig.json, so the linter checks types exactly the way your build does. Compiler options like lib, target, strict, module resolution, custom conditions and path mappings all behave the same in both places. A Temporal type is only flagged when your lib really does not load Temporal, and a monorepo that resolves workspace packages from source (behind a source export condition) resolves them at lint time too, instead of reporting false errors on markers over cross package types.

A config that is missing or does not parse is reported as a runtypes/broken-tsconfig error instead of being silently ignored, the same way tsc would refuse to run. Fix the config and the next lint pass picks it up automatically.

By default the config is found the same way tsc finds it, searching upward from the directory the linter runs in. Point it at a different config with a setting:

{
  "settings": {"runtypes": {"tsconfig": "tsconfig.lint.json"}}
}

Checking without a linter

The same findings are available straight from the CLI, which is handy for CI jobs or scripts that do not run node linting:

ts-runtypes compile --no-emit
ts-runtypes enrich src/__runtypes/enriched/friendly/user.ts --no-emit --json
ts-runtypes enrich --no-emit --json

compile --no-emit reports the build's RunType diagnostics (marker and serialization problems) while writing nothing. enrich --no-emit with a single file reports the tag hygiene, content validity, and drift findings for that file (exit 1 on errors), and with a directory, or no path, it walks the whole mirror directory for drift.

Copyright © 2026