I'm trying to enforce having the same translation keys for every language with TypeScript.
const useTranslation<T extends TranslationObject> = (translations: T) => {
// do something
}
const translations = {
en: {
button: "Hello World"
},
de: {
button: "Hallo Welt"
},
}
const invalidTranslations = {
en: {
button: "Hello World"
},
de: {
foo: "Hallo Welt"
},
}
// this should work
useTranslation(translations)
// this should give me a type error because typeof invalidTranslations["en"] !== typeof invalidTranslations["de"]
useTranslation(invalidTranslations)
I just can't figure out how the TranslationObject type should look in order to enforce the same type of all typeof translations[Language]. This is what I have so far:
const languages = ["de", "en"] as const;
type Language = typeof languages[number];
type Translation = { [key: string]: string | Translation };
type TranslationObject = { [K in Language]: Translation };
Note: I'd like to use TypeScript's inference for useTranslation and not explicitly provide any generic types. I do have a working example for providing the generic type:
type UnionToIntersection<T> = (T extends any ? (k: T) => void : never) extends (k: infer I) => void ? I : never;
type IsUnion<T> = [T] extends [UnionToIntersection<T>] ? true : false;
type VerifyTranslations<T extends TranslationObject> = IsUnion<T[Language]> extends true ? T : never;
// "Argument of type '[...]' is not assignable to parameter of type 'never'.ts(2345)"
useTranslation<VerifyTranslations<typeof invalidTranslations>>(invalidTranslations);