Как набрать проверить словари i18n с TypeScript? - PullRequest
1 голос
/ 08 октября 2019

Есть ли возможность ввести проверку существующих ключей в словарях act-i18next ? Так что TS предупредит вас во время компиляции, если ключ не существует.

Пример.

Предположим, у нас есть следующий словарь:

{
  "footer": {
    "copyright": "Some copyrights"
  },

  "header": {
    "logo": "Logo",
    "link": "Link",
  },
}

Если я предоставлю не-существующий ключ, TS должен взорваться:

const { t } = useTranslation();

<span> { t('footer.copyright') } </span> // this is OK, because footer.copyright exists
<span> { t('footer.logo') } </span> // TS BOOM!! there is no footer.logo in dictionary

Какое собственное название этой техники? Я очень уверен, что я не единственный, кто спрашивает об этом поведении.

Это реализовано в react-i18next из коробки? Есть ли в * 1016 API для расширения библиотеки, чтобы включить ее? Я хочу избежать создания функций-оболочек.

1 Ответ

2 голосов
/ 09 октября 2019

Хотя я согласен, что ключи со строгой типизацией были бы очень полезны в i18next, есть две причины, почему это невозможно:

1.) У TypeScript нет способа оценить динамические / вычисляемые строковые выражения подобно 'footer.copyright', поэтому footer и copyright могут быть определены как ключевые части в иерархии объектов перевода.

2.) Реагирует i18next useTranslation API работает со строками (сравнивают типы здесь и здесь ) и не применяет зависимости типа к вашему определенному словарю / переводу. Вместо этого функция t содержит параметры универсального типа, которые по умолчанию равны string или аналогичным расширенным типам, если они не указаны вручную.


Зная, что вы скорее не хотите использовать оболочку, здесьпросто демонстрационный пример, который я собрал некоторое время назад и использующий параметры / кортежи Rest.

Типизированная t функция:

type Dictionary = string | DictionaryObject;
type DictionaryObject = { [K: string]: Dictionary };

interface TypedTFunction<D extends Dictionary> {
    <K extends keyof D>(args: K): D[K];
    <K extends keyof D, K1 extends keyof D[K]>(...args: [K, K1]): D[K][K1];
    <K extends keyof D, K1 extends keyof D[K], K2 extends keyof D[K][K1]>(
        ...args: [K, K1, K2]
    ): D[K][K1][K2];
    // ... up to a reasonable key parameters length of your choice ...
}

Типизированная useTranslation Крюк:

import { useTranslation } from 'react-i18next';

type MyTranslations = {/* your concrete type*/}
// e.g. via const dict = {...}; export type MyTranslations = typeof dict

// import this hook in other modules instead of i18next useTranslation
export function useTypedTranslation(): { t: TypedTFunction<typeof dict> } {
  const { t } = useTranslation();
  // implementation goes here: join keys by dot (depends on your config)
  // and delegate to lib t
  return { t(...keys: string[]) { return t(keys.join(".")) } }  
}

Импорт useTypedTranslation в других модулях:

import { useTypedTranslation } from "./useTypedTranslation"

const App = () => {
  const { t } = useTypedTranslation()
  return <div>{t("footer", "copyright")}</div>
}

Проверьте это:

const res1 = t("footer"); // const res1: { "copyright": string;}
const res2 = t("footer", "copyright"); // const res2: string
const res3 = t("footer", "copyright", "lala"); // error, OK
const res4 = t("lala"); // error, OK
const res5 = t("footer", "lala"); // error, OK

Детская площадка

Возможно, вы могли бы вывод этих типов автоматически с рекурсивными типами вместо приведенных выше сигнатур множественной перегрузки. Но в этом случае команда TS не рекомендует их для производства , поэтому я представляю последнее здесь.

Надеюсь, это поможет.

...