# comvi typegen

Generate a TypeScript declaration from your Comvi project's keys, keep it fresh with --watch, and verify it in CI with --check and its exit codes.

Canonical: https://comvi.io/docs/cli/typegen

Fetch the project's key schema and write a `.d.ts` that types every `t()` call: key names,
required parameters and parameter types.

## Usage

`Terminal`

```bash
comvi typegen [options]
```

## Options

| Option | Default | Meaning |
| --- | --- | --- |
| `-c, --config <path>` | nearest `.comvirc.json` | Config file to read |
| `-w, --watch` | off | Generate once, then subscribe to schema updates over SSE and regenerate on every change |
| `--check` | off | Write nothing; compare the file on disk with the current schema and exit `1` when outdated |

`--check` wins over `--watch` when both are passed. The output path comes from `outputPath`
in the config, default `src/types/i18n.d.ts`; `strictParams: false` makes every parameter
optional, and `augment` names the module to augment. Every field is on
[Comvi CLI](/docs/cli#config-file).

## Examples

`Generate once`

```bash
npx comvi typegen
```

`Regenerate while developing`

```bash
npx comvi typegen --watch
```

`.github/workflows/ci.yml`

```yaml
- run: npx comvi typegen --check
env:
    COMVI_API_KEY: ${{ secrets.COMVI_API_KEY }}
- run: npx tsc --noEmit
```

`Monorepo package with a shared config`

```bash
npx comvi typegen --config ../../.comvirc.json
```

`--watch` regenerates when a translator adds, renames or removes a key; it does not watch
local files. Ctrl-C closes the connection. Regenerating an unchanged schema produces no
diff, so the command is safe in a pre-commit hook or a build step. Without `--config`, the
file is searched upward from the working directory and the search stops at the first
`package.json`, so a config at the repository root is not found from `packages/web`.

## Use the generated file

`src/i18n.ts`

```ts

export const i18n = createI18n({ locale: 'en', fallbackLocale: 'en' });
```

`src/usage.ts`

```ts

i18n.t('hello');
i18n.t('greeting', { name: 'Alice' });
i18n.t('cart.items', { count: 5 });
i18n.t('dashboard.title', { ns: 'admin' });
```

Import the file once, for its side effect, in the module that builds the i18n instance. The
declaration augments the Comvi package your `package.json` declares, so the same import
types `t()` in every binding. Keys from the default namespace lose their prefix; every
other namespace keeps `namespace:key`. The file's structure and the augment rule are on
[Type-check translation keys](/docs/i18n/type-safety#generated-type-structure).

> **Generated types close the key union**
>
> While no declaration is imported, `t()` accepts any string. Once one is, a key outside the
> union is a compile error. A key of another namespace is written bare with the namespace as a
> parameter, `t('dashboard.title', { ns: 'admin' })`, which type-checks in every binding. In
> `@comvi/react`, `@comvi/vue`, `@comvi/svelte`, `@comvi/solid` and `@comvi/next/client`,
> `useI18n('admin')` also types the `t` it returns, so `t('dashboard.title')` compiles there.
> The auto-imported `useI18n('admin')` of `@comvi/nuxt` scopes the lookup at runtime only, so
> pass `{ ns: 'admin' }` there. `t('admin:dashboard.title')` type-checks nowhere and does not
> resolve at runtime either.

## Check mode

`Terminal`

```bash
comvi typegen --check
```

The check fetches the schema, emits what the file should contain and compares it with the
file on disk. A `Generated at:` header line in an older file is ignored.

| Exit | Meaning |
| --- | --- |
| `0` | The file is up to date |
| `1` | Outdated: run `comvi typegen` and commit the result. Also a missing `.comvirc.json` |
| `2` | The check could not run: the API was unreachable, timed out, rejected the key or returned something unusable |
| `4` | Invalid config, or a schema the emitter refuses: an unsupported parameter type, or two keys that collide once the default namespace is stripped |

Treat `1` as a staleness failure for developers and `2` as an infrastructure failure, or
every API blip looks like stale types. A missing config also exits `1`, so a job that
never checked out `.comvirc.json` looks the same as outdated types; check that the file
exists first when the two must differ. Plain `comvi typegen` reports the same emitter
refusal as a failed generation and exits `1`.

## The emitter behind the command

`comvi typegen` and `@comvi/vite-plugin` share one internal emitter, so the CLI in CI and
the Vite plugin in development never disagree about the file. The emitter is bundled into
both tools and not published. To generate from another schema source, use `TypeEmitter`
from `@comvi/cli`, `new TypeEmitter().generate(schema, { strictParams, defaultNsName, augment })`, or shell out
to `comvi typegen`.

## Next steps

- [Type-check translation keys](/docs/i18n/type-safety): the generated structure, typed calls and local files.
- [Read the type errors](/docs/i18n/type-safety#read-the-type-errors): each diagnostic with cause and fix.
- [comvi pull](/docs/cli/pull): fetch the catalogs the types describe.
- [CI/CD integration](/docs/platform/recipes/ci-cd-integration): full pipeline examples.
