Search code examples
typescriptnext.js

Return typed object from json file


In nextjs, I'm using this example to load translations and return them into the component

import "server-only";
import i18nConfig from "../../i18n-config";

const dictionaries = {
  en: () =>
    import("../../dictionaries/en.json").then((module) => module.default),
  hr: () =>
    import("../../dictionaries/hr.json").then((module) => module.default),
};

type TDictionary = (typeof dictionaries)["en"];

export const getDictionary = async (locale: string): Promise<TDictionary> =>
  dictionaries[
    i18nConfig.locales.includes(locale) ? locale : i18nConfig.defaultLocale
  ]();
export default async function Navbar ( props: ILangParams ) {
  const dict = await getDictionary(props.params.lang); 

  return (
    <nav className={styles.navbar}>
      <Link href="/" className={styles.linkLogo}>
        <Image
          priority
          src={logo}
          alt={dict.navbar.links.home}
          className={styles.logo}
        />
    </nav>
  );
}

If I don't type getDictionary to return Promise<TDictionary>, it returns as Promise<any>.

With Promise<TDictionary>, it returns

const dict: () => Promise<{
    navbar: {
        links: {
            logoAlt: string;
            escapeGames: string;
            becomeAPartner: string;
            bookDemo: string;
        };
    };
}>

This seems wrong as I cannot use dict.navbar.links.home to get the translated string.

How do I properly type this?

the structure is the same for all translations, so I only have to cast it as one of those files ((typeof dictionaries)["en"])


Solution

  • I have a piece of code that works, it's pretty close to the one you wrote.

    export type Lang = "fr" | "en" | "zh"
    
    const dictionaries = {
        fr: () => import('./dictionaries/fr.json').then((module) => module.default),
        en: () => import('./dictionaries/en.json').then((module) => module.default),
        zh: () => import('./dictionaries/zh.json').then((module) => module.default),
    }
    
    type TDictionary = ReturnType<(typeof dictionaries)["fr"]>; // has to be the return type 
    
    // The type of lang has to be union type
    // otherwise you will get the error "Element implicitly has an 'any' type"
    export default async function getDictionary(lang: Lang): Promise<TDictionary> {
        return dictionaries[lang]();
    }