Making TypeScript Errors Actually Helpful
When building my own typesafe i18n library, TypeScript's default errors became hard to work with. I learned how to create custom error messages that tell you exactly what's wrong and how to fix it.
I built a typesafe i18n library for Tilly. When comparing objects with hundreds of translation keys, TypeScript’s default errors became overwhelming. Missing keys? Wall of text. Wrong parameters? Good luck finding them.
Then I learned that you can use a small TypeScript trick to generate custom error messages that tell you exactly what’s wrong.
The Discovery
With conditional types you can create an else
branch that contains a custom string type:
type RequireGreeting<T extends string> = T extends `Hello ${string}` ? T : `❌ String must start with "Hello", got "${T}"`
let bad: RequireGreeting<"Hi there"> = "Hi there"// ❌ Type '"Hi there"' is not assignable to type// '"❌ String must start with "Hello", got "Hi there"'
The error message literally includes our custom string. Even better—you can use template literals to inject type information directly into the error. The "${T}"
shows the actual value that caused the problem.
This transforms generic type mismatches into actionable error messages.
Real Examples from @ccssmnn/intl
Parameter Validation
When translating messages, parameters must match exactly. If the base is "Hello {$name}!"
, the translation needs {$name}
too:
let base = messages({ greeting: "Hello {$name}!", count: "You have {$num :number} items"})
translate(base, { greeting: "Hallo {$firstName}!", // ❌ Wrong parameters: missing {name}; unexpected {firstName} count: "Du hast {$num} Elemente" // ❌ Type mismatches: num expected :number, got :string})
The errors show exactly which parameters are wrong and what types are expected.
Missing Translation Keys
When checking translation coverage across catalogs:
let base = merge(authMessages, profileMessages, settingsMessages)
let german = check(base, authGerman, profileGerman)// ❌ Missing keys: settings.theme, settings.language, settings.notifications
It tells you precisely which keys you forgot to translate.
The Effect
What’s good for humans is even better for AI. When an agent sees exactly which parameters are missing, which types are wrong, or which keys need translation, it can fix the problem without guessing.
This level of type safety and actionable errors allows me to let AI agents translate my app as features evolve. I use this heavily when building Tilly. The TypeScript-native i18n library turned messages into typesafe code, making autonomous translation updates possible. It’s open source at @ccssmnn/intl.
Whenever you do fancy TypeScript, consider adding actionable error messages.