Reputation: 368
I'm trying to create a translation module using typescript.
I want to define the languages as an enum parameter to create-text function, as like:
export enum Language {
He = "he",
En = "en",
}
const { createI18n, createI18nText } = createTextFunctions(Language);
const firstExample = createI18nText({
he: {
firstText: "שלום",
sc: {
hello: "שלום שוב"
}
},
en: {
firstText: "hello",
sc: {
hello: "hello again"
}
}
})
export const i18n = createI18n({
welcome: firstExample,
})
But my problem is that because the languages are are passed as params to a typescript function and the function infers the types, typescript is not alarming anything. I can create text with non-existing language and it will pass it,like createI18nText({ ar:{ hi : "hi" }})
.
My text functions are these:
export type Languages = { [key: string]: string };
export const createTextFunctions = (languages: LanguagesD) => {
type I18nText<T extends object> = {
[k in keyof typeof languages]: T;
}
const createI18n = <T extends { [key: string]: I18nText<any>; }>(i18n: T) => {
return i18n;
};
const createI18nText = <T extends object>(text: I18nText<T>) => {
return text;
}
return {
createI18n,
createI18nText
}
}
So the code is running and doing whatever it needs to do, but I'm losing type control.
I do prefer to have my enum values lower-cased, so its an issue too. If this is the solution so I'll take it, but if there is any way to pass an enum-param and to run by its values it would be great.
Upvotes: 2
Views: 268
Reputation: 4278
You can make createTextFunctions
use a generic. You will be able to customise the keys as you want when you create the text function :
// *L* must be a union of strings.
const createTextFunctions = <L extends string>() => {
type I18nText<T extends object> = Record<L, T>;
type I18n = Record<string, I18nText<any>>;
const createI18n = <T extends I18n>(i18n: T): T => {
return i18n;
};
const createI18nText = <T extends object>(text: I18nText<T>): I18nText<T> => {
return text;
}
return {
createI18n,
createI18nText
}
}
Then specify the Language
as a union of strings:
type Language = "en" | "he";
And create/use the text functions:
const { createI18n, createI18nText } = createTextFunctions<Language>();
const firstExample = createI18nText({
he: {
firstText: "שלום",
sc: {
hello: "שלום שוב"
}
},
// If the key *en* is missing, typescript will complain.
en: {
firstText: "hello",
sc: {
hello: "hello again"
}
},
// If we add the key *us*, typescript will complain.
})
export const i18n = createI18n({
welcome: firstExample,
})
IMO union types are more comfortable to use than enums.
Upvotes: 1