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
{
"hello": "Hello",
"welcome": "Welcome, {name}!",
"items_count": "{count, plural, one {# item} other {# items}}"
}Output: src/generated/i18n/messages.ts
// 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:
| Pattern | Inferred Type |
|---|---|
{name} | string |
{count, number} | number |
{count, plural, ...} | number |
{date, date} | Date (coming soon) |
Compile-Time Errors
TypeScript catches errors at compile time:
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:
- Translation keys — Type
m.and see all available translations - Parameters — Type
{ }and see required parameters - Documentation — JSDoc comments show the default value
m.wel// IDE suggests: welcome
m.welcome({ n// IDE suggests: nameThe m Object
All translation functions are exported both individually and as part of the m object:
// 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:
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 accessnoImplicitAny— Requires explicit typesstrictNullChecks— 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:
# Don't ignore generated i18n files
!src/generated/i18n/2. Add a Pre-commit Hook
Ensure translations are always up-to-date:
// package.json
{
"scripts": {
"precommit": "typeglot build && git add src/generated/i18n"
}
}3. Use Strict TypeScript Config
Ensure your tsconfig.json has strict mode enabled:
{
"compilerOptions": {
"strict": true,
"noUncheckedIndexedAccess": true
}
}