Skip to main content
View as Markdown

TypeScript Configs

A config can be a TypeScript module instead of a YAML or JSON file. The CLI takes app.ts everywhere it takes app.yaml. The difference is entirely in your editor, where every property, field type and component type is checked as you type.

There is no npm in this. A TypeScript config needs no package.json, no node_modules and no install step. The types ship inside the binary, and one command writes them next to your config.

Set it up

Run this in the directory that holds your config:

>_ terminal
sovrium types

Two files land, and both are needed:

File What it does On re-run
sovrium.d.ts Declares the bare sovrium module your config imports from Always rewritten
tsconfig.json Puts that declaration in the TypeScript program Written if absent

The declaration on its own is inert. With no tsconfig.json, tsc never pulls it into the program and every config fails with TS2307: Cannot find module 'sovrium'.

Now write the config:

app.ts
// app.ts
import type { AppConfig } from 'sovrium'

export default {
  name: 'my-app',
  version: '1.0.0',
  description: 'A simple todo list',
  tables: [
    {
      id: 1,
      name: 'tasks',
      fields: [
        { id: 1, name: 'title', type: 'single-line-text', required: true },
        { id: 2, name: 'done', type: 'checkbox', default: false },
      ],
    },
  ],
} satisfies AppConfig

Run it with the same commands as any other format:

>_ terminal
sovrium start app.ts
sovrium validate app.ts

To get all three files at once, scaffold with sovrium init --typescript. It writes app.ts, sovrium.d.ts and tsconfig.json together, so a fresh directory type-checks and validates on the first try.

Why the import is type-only

The import is import type, and the object is checked with satisfies. Neither is a style preference.

import type is erased before the binary looks. Sovrium leaves bare-package specifiers unresolved, so a value imported from sovrium would have nothing to resolve to at boot. A type-only import disappears at transpile time, so the specifier is never resolved at all. That is what makes a typed config work with nothing installed.

The declaration exports types and never a value. This is deliberate, and it makes the mistake unreachable rather than merely discouraged. A helper function living in that file would type-check clean and then refuse to boot, which is the worst shape a failure can take: tsc exits 0, the config ships, and the error arrives at sovrium start. With no values to import, a value import fails in your editor for the ordinary reason that no such export exists. A build guard fails the release if one ever creeps in.

satisfies beats an AppConfig annotation. It checks the literal against the type without widening it, so the export keeps its precise shape and a misspelled property is still an excess-property error.

Composing with imports

$ref is a YAML and JSON mechanism. TypeScript already has one. The declaration exports a type per section, so split the config into modules and assemble them:

app.ts
// config/tables.ts
import type { TableConfig } from 'sovrium'

export const companies: TableConfig = {
  id: 1,
  name: 'Companies',
  fields: [
    { id: 1, name: 'name', type: 'single-line-text', required: true },
    { id: 2, name: 'website', type: 'url' },
  ],
}
app.ts
// app.ts
import type { AppConfig } from 'sovrium'
import { companies } from './config/tables'

export default {
  name: 'crm-workspace',
  tables: [companies],
} satisfies AppConfig

TableConfig, PageConfig, AuthConfig, ThemeConfig and the other section types all come from the same declaration. And because the config is a real module, it can be computed: read a value from the environment, generate a table per entity, derive routes from a list.

After upgrading the binary

Re-run sovrium types. The declaration describes the schema of the binary that wrote it, so a stale one quietly disagrees with the engine you are now running. Rewriting it on every run is what makes a skew between your types and your binary impossible to reach.

Your tsconfig.json is left alone, because by then it is yours: paths, JSX, stricter compiler flags. One thing has to stay true of it, and the command says so when it declines to touch it. sovrium.d.ts must remain in the TypeScript program, which it is unless an include or files entry narrows the default glob past it.

When to prefer TypeScript

Choose TypeScript when Choose YAML when
The config is large enough that typos cost real debugging time The config is small and read more often than edited
Sections repeat and you would rather generate than copy Non-developers need to read or amend it
Values come from the environment or another source You want the file to be obviously data, not code
You want errors in the editor rather than at sovrium validate Nobody editing it runs a TypeScript-aware editor

Both describe the same object, so this is not a one-way door. A YAML config transcribes to TypeScript by hand, and the reverse.

Last updated September 1, 2026

This documentation was written with AI, so errors or outdated content are possible. Sovrium is in beta. Contributions and corrections are welcome.

Built with Sovrium