---
title: "verbatra types"
description: "Génère des déclarations TypeScript pour les clés de ton catalogue et les arguments de leurs messages, sans appeler de fournisseur ni lire de clé d'API."
type: "reference"
---
# verbatra types

Génère des déclarations TypeScript pour les clés de ton catalogue et les arguments de leurs messages, sans appeler de fournisseur ni lire de clé d'API.

<AvailableFrom version="0.11.0" />

Transforme une clé mal orthographiée ou un argument d'interpolation oublié en erreur de compilation. `types` lit ton catalogue source via l'[adaptateur de format](https://verbatra.kreitz-webdev.de/fr/docs/formats.md) configuré et écrit un fichier de déclarations : une union de toutes les clés et, pour chaque clé, les arguments que prend son message. Il n'appelle aucun fournisseur, ne lit aucune clé d'API et ne touche jamais un fichier de locale cible.

## Synopsis [#synopsis]

```bash
verbatra types [flags]
```

## Exemples [#exemples]

```bash
# write verbatra-types.d.ts from the source catalog
verbatra types

# write it where the app already imports from
verbatra types --out src/generated/messages.d.ts

# CI gate: exit 1 if the committed declaration is stale
verbatra types --check

# machine-readable result for a script
verbatra types --json
```

## Options [#options]

| Option | Argument | Défaut | Effet |
| --- | --- | --- | --- |
| `--out` | `<path>` | `verbatra-types.d.ts` | où écrire la déclaration, relativement au répertoire de travail et à l'intérieur de la racine du projet ; les répertoires manquants sont créés |
| `--check` | aucun | désactivé | compare octet par octet la déclaration commitée à une génération fraîche, sans rien écrire ; sortie `1` si elle est périmée ou manquante |

Accepte aussi les [options globales](https://verbatra.kreitz-webdev.de/fr/docs/cli.md#options-globales) `--cwd`, `--config` et `--json`.

## Comportement [#comportement]

### Ce qui est généré [#ce-qui-est-généré]

À partir d'un `locales/en.json` comme celui-ci :

```json
{
  "app": { "title": "Verbatra" },
  "greeting": "Hello {{name}}",
  "cart": { "item_one": "{{count}} item", "item_other": "{{count}} items" }
}
```

tu obtiens ceci :

```ts
// Generated by verbatra from locales/en.json (i18next-json). Do not edit by hand.
// Re-run `verbatra types` after the source catalog changes.

/** An argument whose type the source catalog does not record. */
export type VerbatraArgument = string | number;

/** The arguments of a message that takes none: passing any argument is a type error. */
export type VerbatraNoArguments = Record<string, never>;

/**
 * The arguments of a message verbatra could not analyse, so nothing is claimed about them.
 * Every key carrying this type is listed by `verbatra types` when it generates this file.
 */
export type VerbatraUnknownArguments = Record<string, VerbatraArgument>;

/** Every key in the source catalog, mapped to the arguments its message takes. */
export interface VerbatraMessages {
  "app.title": VerbatraNoArguments;
  "greeting": { readonly "name": VerbatraArgument };
  "cart.item": { readonly "count": VerbatraArgument };
  "cart.item_one": { readonly "count": VerbatraArgument };
  "cart.item_other": { readonly "count": VerbatraArgument };
}

/** Every key in the source catalog. */
export type VerbatraMessageKey = keyof VerbatraMessages;

/** The arguments the message at `Key` takes. */
export type VerbatraMessageArguments<Key extends VerbatraMessageKey> =
  VerbatraMessages[Key];

/** The keys the source catalog marks as carrying plural forms. */
export type VerbatraPluralMessageKey = "cart.item_one" | "cart.item_other";
```

Type ta propre fonction de traduction avec, et le compilateur fait le reste :

```ts
import type { VerbatraMessageArguments, VerbatraMessageKey } from "./verbatra-types.js";

declare function t<Key extends VerbatraMessageKey>(
  key: Key,
  args: VerbatraMessageArguments<Key>,
): string;

t("greeting", { name: "Ada" }); // fine
t("greting", { name: "Ada" });  // error: not a key in the catalog
t("greeting", {});              // error: the name argument is required
t("app.title", { name: "Ada" }); // error: this message takes no argument
```

### Ce qui est affirmé et ce qui ne l'est pas [#ce-qui-est-affirmé-et-ce-qui-ne-lest-pas]

Les clés sont exactement celles que l'adaptateur de format a produites, dans l'ordre du document, donc le même catalogue produit toujours les mêmes octets. Les arguments viennent des jetons de placeholders que l'adaptateur a extraits ; les messages `next-intl-json` et `arb` sont lus avec l'analyseur ICU de l'adaptateur, donc un argument que seules certaines branches utilisent est quand même déclaré. Rien n'est deviné, et rien n'est déclaré en `any`.

| Ton message | Ce qui est déclaré |
| --- | --- |
| aucun placeholder | `VerbatraNoArguments`, donc passer quoi que ce soit est une erreur de type |
| des placeholders nommés (`{{name}}`, `{name}`, `%(name)s`) | une forme d'objet portant ces noms |
| des placeholders numérotés ou anonymes (`{0}`, `%@`, `%1$d`) | un tuple `readonly`, une position par argument |
| un argument que seules certaines branches `select` ou `plural` utilisent (`next-intl-json`, `arb`) | un membre optionnel, `name?:`, ou une position de tuple optionnelle en fin de tuple (`[number, VerbatraArgument?]`) ; un argument utilisé par toutes les branches reste obligatoire |
| un argument ICU `number`, `plural` ou `selectordinal` dans `next-intl-json` ou `arb` | `number` |
| un argument ICU `date` ou `time` (`{d, date, short}`) dans `next-intl-json` ou `arb` | `Date \| number` |
| un argument ICU `select` (`{gender, select, ...}`) dans `next-intl-json` ou `arb` | `string` |
| un formateur i18next `number` (`{{count, number}}`) dans `i18next-json`, `ngx-translate-json` ou `yaml` | `number` |
| un formateur i18next `datetime` (`{{d, datetime}}`) dans ces trois mêmes formats | `Date \| number` |
| une interpolation i18next non échappée (`{{- name}}`) | le nom derrière le préfixe, `name` |
| un argument MessageFormat `number`, `plural` ou `choice` (`{0,number}`) dans `properties` | `number` |
| une conversion printf numérique (`%d`, `%1$d`) | `number` |
| une conversion printf de chaîne (`%s`, `%(name)s`) | `string` |
| tout autre placeholder, y compris un argument ICU simple (`{name}`) | `VerbatraArgument`, un alias de `string \| number` |
| une syntaxe de message invalide, que seuls `next-intl-json` et `arb` vérifient | `VerbatraUnknownArguments`, et la clé est nommée dans la sortie |
| un message qui mélange arguments nommés, numérotés et anonymes | `VerbatraUnknownArguments`, et la clé est nommée dans la sortie |
| un argument numéroté sous la position 0 ou à partir de la position 64 (`%0$s`, `{64}`) | `VerbatraUnknownArguments`, et la clé est nommée dans la sortie |

Un format dont les placeholders ne portent pas de nom (Apple `.strings`, Android `strings.xml`, conversions gettext positionnelles) reçoit un tuple, jamais des noms inventés. Un nom utilisé avec des types différents reçoit leur union : `{d, date, short}` à côté d'un `{d}` simple devient `Date | number | string`. Les clés sont toujours des littéraux de chaîne entre guillemets, donc une clé avec un point, un mot réservé, un chiffre initial ou un guillemet est déclarée telle quelle. Une clé que l'adaptateur a exclue de la traduction (une feuille qui n'est pas une chaîne, une ressource Android `translatable="false"`) n'est jamais déclarée et est nommée dans la sortie.

### Une clé dont le nom contient lui-même un point [#une-clé-dont-le-nom-contient-lui-même-un-point]

La clé déclarée s'écrit comme verbatra l'écrit dans `verbatra.lock.json`, pas dans une syntaxe de recherche que ta bibliothèque i18n comprend. Un catalogue JSON contenant `{"a.b": "..."}` porte la clé `a\.b`, volontairement distincte du chemin imbriqué `a.b` :

```ts
export interface VerbatraMessages {
  "a\\.b": VerbatraNoArguments;
}
```

i18next n'a pas d'échappement pour son séparateur de clés, donc `t("a\\.b")` ne cherche pas cette clé. Si ton catalogue porte de telles clés, règle `keySeparator` sur un autre caractère (ou sur `false` pour un catalogue plat) et fais correspondre la clé déclarée à la clé du runtime dans ton propre wrapper typé.

### Le seul cas où la déclaration se trompe au lieu de simplement se taire [#le-seul-cas-où-la-déclaration-se-trompe-au-lieu-de-simplement-se-taire]

Seuls `next-intl-json` et `arb` valident la syntaxe des messages. Dans tout autre format, une valeur cassée comme `"Hello {name"` ne produit aucun jeton de placeholder : le message est donc déclaré sans argument, et lui passer l'argument dont il a vraiment besoin devient une erreur de compilation. Rien ne le signale, puisque rien ne l'a détecté. Corrige le catalogue, pas la déclaration.

### Clés au pluriel [#clés-au-pluriel]

Rien n'est regroupé. i18next porte le pluriel comme suffixe de clé, donc `cart.item_one` et `cart.item_other` sont déclarées telles qu'elles sont écrites et, à côté, la clé de base `cart.item` que cherche `t("cart.item", { count })`, qui exige `count` et chaque argument que prend l'une des formes. Les types propres à i18next acceptent eux aussi les deux écritures. Un groupe ordinal comme `place_ordinal_one` déclare `place`, et une clé de base que le catalogue contient déjà garde sa propre déclaration. `VerbatraPluralMessageKey` liste les clés que l'adaptateur a marquées comme portant des formes plurielles, celles avec suffixe, pour que tu puisses t'y restreindre.

### Où atterrit la déclaration [#où-atterrit-la-déclaration]

Par défaut elle atterrit dans `verbatra-types.d.ts` à la racine de ton projet. **Commite-la** : un collègue ou un job de CI vérifie les types dessus sans lancer verbatra, et `--check` a besoin d'un fichier commité auquel se comparer. Rien ne l'ajoute au `.gitignore`.

Un chemin de sortie est refusé avec `TYPES_OUTPUT_CONFLICT` quand il :

- ne nomme aucun fichier, y compris quand il se termine par un séparateur de chemin comme `generated/` (un `--out` vide échoue plus tôt, avec `INVALID_OUT`),
- est absolu,
- sort de la racine du projet avec `..`,
- n'est pas un fichier TypeScript, c'est-à-dire que son nom ne se termine pas par `.ts`, `.mts` ou `.cts`,
- nomme un fichier de locale configuré, source ou cible, pour que la génération n'écrase jamais un catalogue,
- est le fichier de verrouillage, `verbatra.lock.json`,
- est le fichier de provenance, `verbatra.provenance.json`,
- est le cache de mémoire de traduction, `verbatra.cache.json`,
- est un fichier où verbatra cherche sa configuration : `package.json`, `.verbatrarc`, `.verbatrarc.json`, `.verbatrarc.yaml`, `.verbatrarc.yml`, `.verbatrarc.js`, `.verbatrarc.cjs`, `.verbatrarc.ts`, `verbatra.config.js`, `verbatra.config.cjs` ou `verbatra.config.ts`,
- est le fichier de configuration que l'exécution a réellement chargé, y compris celui que tu as indiqué avec `--config`,
- est le fichier de glossaire vers lequel pointe `glossary` dans ta configuration,
- est un fichier existant qui ne commence pas par la ligne d'en-tête `// Generated by verbatra from` que verbatra écrit en tête de chaque déclaration (une marque d'ordre des octets et des lignes vides en tête sont ignorées), ou est trop volumineux pour être vérifié.

Les noms de fichiers sont comparés sans tenir compte de la casse, et les vérifications sont refaites une fois les liens symboliques résolus, si bien qu'un répertoire lié ne peut porter la déclaration ni hors de la racine du projet ni sur un fichier protégé. La dernière vérification n'a lieu que si une exécution allait écrire, et laisse ton fichier intact ; `--check` ne refuse jamais un fichier existant. Un chemin qui passe toutes les vérifications peut encore échouer à l'écriture (un répertoire en lecture seule, un disque plein) : l'exécution s'arrête avec `TYPES_UNWRITABLE` et nomme le fichier et le code du système de fichiers, comme `EACCES`.

### Rester honnête en CI [#rester-honnête-en-ci]

Lance `types --check` à côté de [`verbatra check`](https://verbatra.kreitz-webdev.de/fr/docs/cli/check.md) dans le même job : `check` repère les locales qui prennent du retard sur la source, `types --check` tes types. La comparaison se fait octet par octet et la déclaration est toujours écrite avec des fins de ligne line feed, alors fixe-les dans `.gitattributes`, sinon un checkout qui les réécrit laisse le fichier périmé pour de bon :

```text
verbatra-types.d.ts text eol=lf
```

### Sortie JSON [#sortie-json]

Schéma : [`types-envelope.json`](https://verbatra.kreitz-webdev.de/schema/v1/types-envelope.json) valide l'enveloppe entière ; [Schémas](https://verbatra.kreitz-webdev.de/fr/docs/cli/output.md#schémas) liste les autres.

Avec `--json`, l'[enveloppe](https://verbatra.kreitz-webdev.de/fr/docs/cli/output.md#sortie-json) porte un résultat :

```ts
interface GenerateTypesResult {
  path: string;               // absolute path of the declaration file
  sourcePath: string;         // the source catalog, relative to the project root
  keys: number;               // how many keys are declared
  withArguments: number;      // keys taking at least one determined argument
  unresolved: { key: string; reason: "invalid-message-syntax" | "mixed-argument-styles" | "argument-index-out-of-range" }[];
  excluded: string[];         // keys the adapter excluded, never declared
  plural: string[];           // keys carrying plural forms
  written: boolean;           // false under --check and when the file already matched
  stale: boolean;             // the file on disk differed when the run started
  missing: boolean;           // no file existed when the run started
  check: boolean;             // whether this was a --check run
}
```

### Retour dans le terminal [#retour-dans-le-terminal]

<AvailableFrom version="0.12.0" />

Le résumé va sur stdout ; une ligne de spinner et, après l'écriture, une suggestion de committer le fichier vont sur stderr (`--check` ne suggère rien, et `--quiet` supprime les deux). Une exécution qui génère, une relance sans changement et un `--check` périmé :

```text output
verbatra: generating the declarations... done (0.2s)
verbatra types
  214 keys declared, 63 of them taking arguments, from locales/en.json
  arguments not determined (1):
    legal.notice  invalid-message-syntax
  excluded by the adapter (1): meta.version
  wrote verbatra-types.d.ts
next: commit verbatra-types.d.ts (verbatra types --check compares against it in CI)

verbatra types
  214 keys declared, 63 of them taking arguments, from locales/en.json
  unchanged verbatra-types.d.ts

verbatra types
  215 keys declared, 63 of them taking arguments, from locales/en.json
  verbatra-types.d.ts is out of date, re-run verbatra types
```

Consulte [Sortie dans le terminal](https://verbatra.kreitz-webdev.de/fr/docs/cli/output.md#sortie-dans-le-terminal) pour la couleur, le spinner et les variables qui les contrôlent.

## Codes de sortie [#codes-de-sortie]

| Code | Signification |
| --- | --- |
| `0` | la déclaration a été générée, que le fichier ait changé ou non ; ou `--check` l'a trouvée à jour |
| `1` | `--check` a trouvé la déclaration commitée périmée ou manquante (`is missing, run verbatra types to create it`) |
| `2` | n'a pas pu s'exécuter : une erreur d'utilisation, un problème de configuration ou de source, un chemin de sortie refusé (`TYPES_OUTPUT_CONFLICT`), ou une déclaration qui n'a pas pu être écrite (`TYPES_UNWRITABLE`) |

Consulte [Codes de sortie](https://verbatra.kreitz-webdev.de/fr/docs/cli/output.md#codes-de-sortie) pour le contrat que suit chaque commande.

## Voir aussi [#voir-aussi]

- [`verbatra extract`](https://verbatra.kreitz-webdev.de/fr/docs/cli/extract.md) ajoute au catalogue les clés que ton code utilise ; `types` les déclare ensuite.
- [`verbatra check`](https://verbatra.kreitz-webdev.de/fr/docs/cli/check.md) est le contrôle de CI pour la dérive des locales, à côté de celui-ci pour la dérive des types.
- [Formats](https://verbatra.kreitz-webdev.de/fr/docs/formats.md) liste la syntaxe de placeholders de chaque format.
- [Codes de sortie et sortie JSON](https://verbatra.kreitz-webdev.de/fr/docs/cli/output.md) couvre l'enveloppe et le contrat des codes de sortie.
