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

## `@sovrium/types`

The types come from a separate package, so authoring a config never pulls the engine into your project.

```bash
bun add -d @sovrium/types
```

```typescript
// app.ts
import { defineConfig } from '@sovrium/types'

export default defineConfig({
  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 },
      ],
    },
  ],
})
```

```bash
sovrium start app.ts
sovrium validate app.ts
```

`defineConfig` is an identity function: it returns its argument untouched and annotates it as `AppConfig`. That is all it needs to do — the annotation is what makes an invalid field type, an unknown component type, or a missing required property an error at the keystroke rather than at boot.

:::callout
**Zero runtime, MIT-licensed.** `@sovrium/types` ships only `.d.ts` files, generated from the same `AppSchema` the server decodes. Nothing from it reaches your bundle, and it is not the engine — for `import { start } from 'sovrium'` see the [TypeScript API](/en/docs/typescript).
:::

## Composing with imports

`$ref` is a YAML/JSON mechanism; TypeScript already has one. Split sections into modules and assemble them in `defineConfig`:

```typescript
// config/tables.ts
import type { TableConfig } from '@sovrium/types'

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' },
  ],
}
```

```typescript
// app.ts
import { defineConfig } from '@sovrium/types'
import { companies } from './config/tables'

export default defineConfig({
  name: 'crm-workspace',
  tables: [companies],
})
```

Because it is a real module, the config can also be computed — read a value from the environment, generate a table per entity, derive routes from a list.

## 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` | You want it diffable and reviewable without a build |

Both are the same object, so this is not a one-way door: a YAML config transcribes to TypeScript by hand, and vice versa.

## Related Pages

- [Config Files: YAML & JSON](/en/docs/configuration-files) — the other two formats.
- [Multi-File Configs](/en/docs/configuration-refs) — the YAML/JSON equivalent of imports.
- [JSON Schema](/en/docs/json-schema) — the same guarantees for YAML authors.
- [TypeScript API](/en/docs/typescript) — running Sovrium as a library.
