Reputation:
I am trying to dynamically index a record with a known type using a string which can possibly be typed too, below is all the relevant code including the error which shows up inside my reduce (acc[val]
), I am having troubles understanding the error so I do not know how to fix it
interface CommandsPingDescription {
type: string;
props: never;
}
interface LocalizationMap {
"commands.ping.description": CommandsPingDescription;
}
import base from "../locale/base.json";
import en from "../locale/en.json";
type Language = keyof typeof languages;
const languages: Record<"en", typeof base> = { en };
export const setLanguage = <T extends Language>(lang: T) => {
return <
K extends keyof LocalizationMap,
V extends LocalizationMap[K]["props"]
>(
key: K,
...placeholders: V[]
) => {
let translation = key
.split(".")
.reduce((acc, val) => acc[val], languages[lang]);
(parameter) acc: Record<"en", {
commands: {
ping: {
description: string;
};
};
}>[T]
Element implicitly has an 'any' type because expression of type 'string' can't be used to index type '{ commands: { ping: { description: string; }; }; }'.
No index signature with a parameter of type 'string' was found on type '{ commands: { ping: { description: string; }; }; }'.
Upvotes: 1
Views: 655
Reputation: 2679
It seems like the meat of your code is here:
const key = "commands.ping.description";
const en = {
commands: {
ping: {
description: "";
}
}
}
let translation = key
.split(".")
.reduce((acc, val) => acc[val], en);
and what you want TypeScript to do is to figure out the type of acc[val]
where
val
is a substring of key
. That's not going to happen; it's well beyond the limitations of string literal types, and for good reason.
What I recommend you do is loosen your types to something like this:
const key = "commands.ping.description";
interface RecursiveRecord<T> {
[key: string]: T | RecursiveRecord<T>;
}
const en: RecursiveRecord<string> = {
commands: {
ping: {
description: "hello";
}
}
}
// go through the properties from key.split(".")
// and assign the string at the end to translation
let record = en;
let translation: string = "";
for (const substr of key.split(".")) {
const oneLevelDeeper = record[substr];
if (typeof oneLevelDeeper === "string") {
translation = oneLevelDeeper
break;
} else {
record = oneLevelDeeper;
}
}
console.log(translation); // "hello"
Upvotes: 2