Skip to content

Type Safety

One of TypeGlot's core features is generating fully-typed TypeScript from your translation files. This eliminates runtime errors from typos and missing parameters.

Generated Code

When you run typeglot build, the compiler generates TypeScript functions for each translation key:

Input: locales/en.json

json
{
  "hello": "Hello",
  "welcome": "Welcome, {name}!",
  "items_count": "{count, plural, one {# item} other {# items}}"
}

Output: src/generated/i18n/messages.ts

typescript
// Auto-generated by @typeglot/compiler
// Do not edit manually

export function hello(): string {
  return messages['hello'] ?? 'Hello';
}

export function welcome(params: { name: string }): string {
  const template = messages['welcome'] ?? 'Welcome, {name}!';
  return formatMessage(template, params);
}

export function items_count(params: { count: number }): string {
  const template = messages['items_count'] ?? '{count, plural, one {# item} other {# items}}';
  return formatMessage(template, params);
}

export const m = { hello, welcome, items_count } as const;

Type Inference

Parameter Types

The compiler infers parameter types from the translation value:

PatternInferred Type
{name}string
{count, number}number
{count, plural, ...}number
{date, date}Date (coming soon)

Compile-Time Errors

TypeScript catches errors at compile time:

typescript
import { m } from './generated/i18n';

// ✅ Correct usage
m.welcome({ name: 'Alice' });

// ❌ Compile error: Property 'naam' does not exist
m.welcome({ naam: 'Alice' });

// ❌ Compile error: Property 'name' is missing
m.welcome({});

// ❌ Compile error: Expected 1 argument, but got 0
m.welcome();

// ❌ Compile error: Type 'number' is not assignable to type 'string'
m.welcome({ name: 42 });

Autocomplete

Your IDE provides full autocomplete for:

  1. Translation keys — Type m. and see all available translations
  2. Parameters — Type { } and see required parameters
  3. Documentation — JSDoc comments show the default value
typescript
m.wel// IDE suggests: welcome
m.welcome({ n// IDE suggests: name

The m Object

All translation functions are exported both individually and as part of the m object:

typescript
// Individual imports
import { hello, welcome } from './generated/i18n';

// Or use the m object (recommended)
import { m } from './generated/i18n';

// Both work the same
hello() === m.hello();
welcome({ name: 'Alice' }) === m.welcome({ name: 'Alice' });

The m object is typed with as const, providing:

  • Autocomplete for all keys
  • Type safety for all parameters
  • Immutability guarantees

Locale Switching

The generated code supports runtime locale switching:

typescript
import { setLocale, getLocale, loadMessages, m } from './generated/i18n';
import * as es from './generated/i18n/es';
import * as fr from './generated/i18n/fr';

// Check current locale
console.log(getLocale()); // 'en'

// Switch to Spanish
setLocale('es');
loadMessages(es.messages);

console.log(m.hello()); // 'Hola'

// Switch to French
setLocale('fr');
loadMessages(fr.messages);

console.log(m.hello()); // 'Bonjour'

Strict Mode

For maximum type safety, the generated code uses strict TypeScript options:

  • noUncheckedIndexedAccess — Catches undefined access
  • noImplicitAny — Requires explicit types
  • strictNullChecks — Catches null/undefined issues

Best Practices

1. Keep Generated Files in Git

Commit the generated files so your CI/CD doesn't need to run the compiler:

gitignore
# Don't ignore generated i18n files
!src/generated/i18n/

2. Add a Pre-commit Hook

Ensure translations are always up-to-date:

json
// package.json
{
  "scripts": {
    "precommit": "typeglot build && git add src/generated/i18n"
  }
}

3. Use Strict TypeScript Config

Ensure your tsconfig.json has strict mode enabled:

json
{
  "compilerOptions": {
    "strict": true,
    "noUncheckedIndexedAccess": true
  }
}

Released under the MIT License.