Configuration
RunTypes reads its settings from your tsconfig.json, in the same plugins list TypeScript already understands. You configure a project in one place, and every build tool (Vite, Rollup, webpack, Rspack, esbuild) picks it up for free.
Options
Every option is optional. Leave one out and the compiler uses its default. Set them in your tsconfig plugin entry, shown under Setting your options below. The one marked "bundler plugin only" is the path used to find your tsconfig, so it cannot live inside it.
| Option | Default | What it does |
|---|---|---|
genDir | inferred from tsconfig | The RunTypes generated-output folder in your project. Modules generated on every build land under types/ (ignored by git); the enrichment CLI writes its committed files under enriched/ (friendly/, mock/, i18n/<locale>/). Each folder carries a README saying what it is. When omitted, it is created next to your source as __runtypes. Also settable in tsconfig as genDir; the CLI flag is --gen-dir. |
emitMode | code | How each generated function ships. code ships the function body as a string and builds it on first use (smallest output). functions ships ready functions. both ships both, for runtimes that block dynamic code (a strict CSP, Cloudflare Workers). |
moduleMode | default | How the generated functions group into modules. default gives your bundler the best code splitting. allSingle groups each feature into one module (fewest files). allModules gives every entry its own module (rarely useful). |
hashLength | 7 | Length of the short ids in generated names. Only raise it if a build ever reports an id collision. |
failOnError | true | Error findings (a type that can never be encoded, a contradictory format rule) stop the build in every lane, including tests and the dev server, just like a type error would. Set false only for a test program that deliberately contains such types; the findings then surface as warnings. |
pureFnReport | off | Emit a structured report of every pure function this build generated, for tooling that moves pure function bodies between two builds of one codebase (a framework that declares a mapper in client code but runs it on the server). On the bundler plugin it is one switch with three states: 'file' writes the report to <genDir>/types/pure-fns-report.json on every build (in the generated folder, so it is gitignored and rewritten like the rest of the output); 'callback' delivers it only in process to the onPureFnReport handler, writing no file; leave it off (the default) for neither. The file location is fixed (like every path under genDir), so it is not configurable. Each record carries the call site, the registry key, which function and package declared the registrar (so a framework wrapper is attributed to the framework, not to @ts-runtypes/core), and the function body itself. The report shape is the same whatever moduleMode you use. In tsconfig, set pureFnReport: true to write the file (the in process handler is a plugin only API); the CLI flags are --pure-fn-report-file (writes the JSON) and --pure-fn-report-wire (report on the wire only). |
size | unset | Tuning for the first buffer a binary encoder allocates, an object detailed in the table below. Leave the key out and the defaults apply. |
i18n | unset | Translation settings for the friendly maps, an object detailed in the table below. Leave the key out entirely and translations are off. |
tsconfig | found like tsc | Path to your tsconfig, relative to the project root. Set it on the bundler plugin, on the lint settings, or with the CLI --tsconfig flag; it is the one option that cannot live inside the tsconfig itself. When not set, the config is found exactly the way tsc finds it, searching upward from the working directory, and a config you name explicitly must load or the tool stops with an error. |
The size object tunes how the first buffer of a binary encoder is sized:
| Key | Default | What it does |
|---|---|---|
bias | 0.8 | How generous that first buffer is, from 0 (tight, may need to grow) to 1 (generous). |
items | 100 | How many elements to assume for an array, Map or Set with no declared limit. |
stringBytes | 32 | How long to assume a string with no declared limit is. |
maxBytes | 65536 | A cap on the estimate, so a very large declared limit never creates a huge buffer. |
The i18n object configures translations for your FriendlyText maps:
| Key | Default | What it does |
|---|---|---|
sourceLocale | en | The language your friendly maps are written in. It decides which plural forms enrich scaffolds in them. |
locales | none | The locales you translate into; enrich --i18n all and its --no-emit check walk this list. Don't list the source locale. |
strict | false | When true, an incomplete or out-of-date translation fails the i18n check by default, the same as passing --require-complete to a single run. Rendering at runtime stays lenient either way. |
onPureFnReport(sites, phase) callback. It fires once with the whole report after the build scan (phase 'build'), and again under Vite with just the changed file when you edit a pure function body (phase 'update'). It carries the same records as the JSON file, so a plugin can consume the report without reading it back from disk. Providing the callback without setting pureFnReport implies 'callback', so just adding a handler turns the report on with no file written.Setting your options
Your tsconfig.json is the home for every option: add an entry named ts-runtypes under compilerOptions.plugins and every build tool (Vite, Rollup, webpack, Rspack, esbuild) picks it up. Every project option also works directly on the @ts-runtypes/devtools bundler plugin, as a per build override for when one build needs to differ from the project baseline. Two kinds of option are the exception: the bootstrap and wire knobs that describe the build itself (tsconfig and the internal options below) are plugin only, and the i18n object is tsconfig only.
{
"compilerOptions": {
"plugins": [
{
"name": "ts-runtypes",
"emitMode": "code",
"moduleMode": "default",
"i18n": {"sourceLocale": "en", "locales": ["es", "pl"]}
}
]
}
}
import {defineConfig} from 'vite';
import runtypes from '@ts-runtypes/devtools/vite';
export default defineConfig({
plugins: [runtypes({emitMode: 'functions'})],
});
@ts-runtypes/devtools first among your enforce: 'pre' plugins, so it sees your source before other plugins rewrite it.Settings layer the way tsc does. A command-line flag or a build-tool option wins over the tsconfig entry, which wins over the built-in default:
build-tool option / CLI flag (highest)
tsconfig plugin entry
built-in default (lowest)
Enrichment sync in dev (bundler plugin)
The enrichment CLI is the authoring path for your committed FriendlyText and MockData maps. If you would rather not run it by hand, the @ts-runtypes/devtools bundler plugin can scaffold and keep those mirrors in sync while your dev server runs. Turn it on with a nested enrich object on the plugin. These keys live only on the bundler plugin, never in your tsconfig plugin entry.
| Key | Default | What it does |
|---|---|---|
friendly | false | Scaffold and sync the enriched/friendly/ maps as your types change. |
mock | false | Scaffold and sync the enriched/mock/ maps as your types change. |
i18n | unset | Sync the per-locale enriched/i18n/<locale>/ mirrors. Takes sourceLocale, locales and strict, the same shape as the tsconfig i18n object; its presence turns the locale sync on. |
suppressHmr | false | Skip HMR for writes under <genDir>/enriched, so the dev server does not reload when the plugin (or you) touch a mirror file. |
Everything defaults off, so a plugin with no enrich object behaves exactly as before. When on, the sync is value-preserving: it lays out and updates the file structure but never fills in translated strings or any other authored value, exactly like enrich --update. It runs only in dev and watch. On a production build the plugin never writes; instead it runs a read-only completeness gate (the build equivalent of enrich --require-complete). It fails the build (when failOnError is on) if any mirror is out of date, missing, or still carries an unfilled blank, so a release can never ship blank labels or mocks. A build never mutates your committed files.
import {defineConfig} from 'vite';
import runtypes from '@ts-runtypes/devtools/vite';
export default defineConfig({
plugins: [
runtypes({
enrich: {
friendly: true,
mock: true,
i18n: {sourceLocale: 'en', locales: ['es', 'pl']},
suppressHmr: true,
},
}),
],
});
The build cache
RunTypes keeps an on-disk artifact cache so a rebuild can skip work for types that did not change. It lives under node_modules/.cache/ts-runtypes, alongside every other tool's cache, and it follows TypeScript's own switch. When your tsconfig sets incremental (or composite), the cache is on. When it does not, the cache is off. There is no separate knob to set, and nothing new to learn: it works the way tsc's own incremental cache already does.
{
"compilerOptions": {
"incremental": true
}
}
node_modules/.cache clear it.Internal options
These exist for developing RunTypes itself, for benchmarking, and for the lint integration. Leave them at their defaults.
| Option | What it does |
|---|---|
inlineMode | How generated functions share code. The default splits them by type, so a shape used in several places is generated once and reused. The alternative (allInternal) inlines everything into each function, which repeats code and grows the output. Do not change it. |
singleThreaded | Runs the compiler on one thread, for debugging. |
parallelScan, parallelRender | Turn off the compiler's parallel phases, for clean benchmark baselines. |
allowUncheckedPatterns | Lets the build pass a format pattern it cannot verify and hands the check to the lint plugin instead. |
binary | Points the plugin at a custom compiler build, used when developing RunTypes itself. |
cwd | Overrides the project root the plugin resolves paths from. |
transformMode | How the rewrite travels between the compiler and the plugin. Both modes produce identical output. |
sourcesContent | Whether the compiler embeds source text in its intermediate source maps. Your final source maps follow your bundler settings either way. |