i18next / react-i18next

Internationalization for react done right. Using the i18next i18n ecosystem.
https://react.i18next.com
MIT License
9.26k stars 1.03k forks source link

TfunKey gives string #1331

Closed ghost closed 3 years ago

ghost commented 3 years ago

Hi ! I've just update the last version of r-i18next. I edit my files to :

import * as Localization from 'expo-localization';
import i18next from 'i18next';
import { initReactI18next } from 'react-i18next';
import { resources } from './translations';

export const defaultNS = 'common';

i18next.use(initReactI18next).init({
  lng: Localization.locale,
  fallbackLng: 'fr-FR',
  supportedLngs: ['fr-FR'],
  load: 'currentOnly',
  defaultNS,
  cleanCode: true,
  resources
});

export default i18next;

and

import { resources } from '~/libs/locales.lib/translations';

declare module 'react-i18next' {
  interface CustomTypeOptions {
    resources: typeof resources['fr-FR'];
  }
}

The t function is correctly typed. But when I want to use TfuncKey, I get :

No overload matches this call.
  Overload 1 of 2, '(key: "MY NS")[], options?: string | ... 1 more ... | undefined): string | ... 30 more ... | { ...; }', gave the following error.
    Argument of type 'string' is not assignable to parameter of type '"MY NS")[]'.
  Overload 2 of 2, '(key: "MY NS")[], defaultValue?: string | undefined, options?: string | ... 1 more ... | undefined): string | ... 30 more ... | { ...; }', gave the following error.
    Argument of type 'string' is not assignable to parameter of type '"MY NS")[]'.ts(2769)

For example, the use case is :


export interface HandledResponse extends SerializedError {
  /**
   * Code permettant de determiner la nature de la réponse.
   */
  code: Flag;
  /**
   * Nom à afficher en titre.
   */
  name: TFuncKey;
  /**
   * Message de la réponse, si besoin.
   */
  message?: TFuncKey;
  /**
   * Messages de la réponse mais en plusieurs exemplaires.
   */
  messages?: TFuncKey[];
}

t(notification.response.message) <- The error

Do you have any idea how to bypass this error ?

adrai commented 3 years ago

//cc @JCQuintas

JCQuintas commented 3 years ago

From what I could test based on your comment, this seems to be an issue regardless of the version. It happens due to having a typed resources but not defaultNS when you use a custom defaultNS

I suppose you can set your default namespace in the CustomTypeOptions as well, since you use a custom one. You can try the following on your .d.ts

import { resources } from '~/libs/locales.lib/translations';

declare module 'react-i18next' {
  interface CustomTypeOptions {
    defaultNS: 'common'; // or typeof defaultNS if you import it
    resources: typeof resources['fr-FR'];
  }
}

Or you can try setting the namespace of the TFuncKey like TFuncKey<'your-namespace'> inside the HandledResponse

If that doesn't work, do you mind setting up a codesandbox or a repository where we can test your issue?

stale[bot] commented 3 years ago

This issue has been automatically marked as stale because it has not had recent activity. It will be closed if no further activity occurs. Thank you for your contributions.

ghost commented 3 years ago

Hi ! Thanks for your reply !

How can I say all namespaces ? Maybe a list ?

EDIT : I have this :

import { TFuncKey } from 'react-i18next';
import { resources } from '~/libs/locales.lib/translations';

declare module 'react-i18next' {
  interface CustomTypeOptions {
    defaultNS: 'common'; // or typeof defaultNS if you import it
    resources: typeof resources['fr-FR'];
  }
}

export type TranslationKeys = TFuncKey<keyof typeof resources['fr-FR']>;

Always :

No overload matches this call.
  Overload 1 of 2, '(key: TemplateStringsArray | Normalize<{ greetings: { goodMorning: string; goodAfternoon: string; }; actions: { startTracking: string; stopTracking: string; }; }> | (TemplateStringsArray | Normalize<...>)[], options?: string | ... 1 more ... | undefined): string | ... 1 more ... | { ...; }', gave the following error.
    Argument of type '"common:actions.stopTracking"' is not assignable to parameter of type 'TemplateStringsArray | Normalize<{ greetings: { goodMorning: string; goodAfternoon: string; }; actions: { startTracking: string; stopTracking: string; }; }> | (TemplateStringsArray | Normalize<...>)[]'.
  Overload 2 of 2, '(key: TemplateStringsArray | Normalize<{ greetings: { goodMorning: string; goodAfternoon: string; }; actions: { startTracking: string; stopTracking: string; }; }> | (TemplateStringsArray | Normalize<...>)[], defaultValue?: string | undefined, options?: string | ... 1 more ... | undefined): string | ... 1 more ... | { ...; }', gave the following error.
    Argument of type '"common:actions.stopTracking"' is not assignable to parameter of type 'TemplateStringsArray | Normalize<{ greetings: { goodMorning: string; goodAfternoon: string; }; actions: { startTracking: string; stopTracking: string; }; }> | (TemplateStringsArray | Normalize<...>)[]'.ts(2769)
pedrodurek commented 3 years ago

This should work:

declare module 'react-i18next' {
  interface CustomTypeOptions {
    defaultNS: 'common'; // or typeof defaultNS if you import it
    resources: typeof resources['fr-FR'];
  }
  type TranslationKeys = TFuncKey<CustomTypeOptions['resources'][]>;
}

Put TranslationKeys inside the module declaration so you can import it from react-i18next, like this: import type {TranslationKeys} from 'react-i18next' or import {TranslationKeys} from 'react-i18next'

ghost commented 3 years ago

Hi ! Thank !

But, I've triied that and I get :

Type '{ common: { greetings: { goodMorning: string; goodAfternoon: string; }; actions: { startTracking: string; stopTracking: string; }; }; 'dashboard.page': { pageName: string; subtitle_last_traject: string; ... 6 more ...; modal: { ...; }; }; ... 7 more ...; notifications: { ...; }; }[]' does not satisfy the constraint 'Namespace<"common" | "dashboard.page" | "form.validation" | "sign.page" | "profil.page" | "traject.page" | "welcome.page" | "home.page" | "feedback.page" | "notifications">'.
  Type '{ common: { greetings: { goodMorning: string; goodAfternoon: string; }; actions: { startTracking: string; stopTracking: string; }; }; 'dashboard.page': { pageName: string; subtitle_last_traject: string; ... 6 more ...; modal: { ...; }; }; ... 7 more ...; notifications: { ...; }; }[]' is not assignable to type '("common" | "dashboard.page" | "form.validation" | "sign.page" | "profil.page" | "traject.page" | "welcome.page" | "home.page" | "feedback.page" | "notifications")[]'.
    Type '{ common: { greetings: { goodMorning: string; goodAfternoon: string; }; actions: { startTracking: string; stopTracking: string; }; }; 'dashboard.page': { pageName: string; subtitle_last_traject: string; ... 6 more ...; modal: { ...; }; }; ... 7 more ...; notifications: { ...; }; }' is not assignable to type '"common" | "dashboard.page" | "form.validation" | "sign.page" | "profil.page" | "traject.page" | "welcome.page" | "home.page" | "feedback.page" | "notifications"'.
      Type '{ common: { greetings: { goodMorning: string; goodAfternoon: string; }; actions: { startTracking: string; stopTracking: string; }; }; 'dashboard.page': { pageName: string; subtitle_last_traject: string; ... 6 more ...; modal: { ...; }; }; ... 7 more ...; notifications: { ...; }; }' is not assignable to type '"notifications"'.ts(2344)
(property) CustomTypeOptions.resources: {
    common: {
        greetings: {
            goodMorning: string;
            goodAfternoon: string;
        };
        actions: {
            startTracking: string;
            stopTracking: string;
        };
    };
    'dashboard.page': {
        pageName: string;
        subtitle_last_traject: string;
        ... 6 more ...;
        modal: {
            ...;
        };
    };
    ... 7 more ...;
    notifications: {
        ...;
    };
}

Do you have any idea ?

Thanks !

ghost commented 3 years ago

Maybe the cause are my translation files ?

My i18n instance is set here :

import * as Localization from 'expo-localization';
import i18next from 'i18next';
import { initReactI18next } from 'react-i18next';
import { resources } from './translations';

export const defaultNS = 'common';

i18next.use(initReactI18next).init({
  lng: Localization.locale,
  fallbackLng: 'fr-FR',
  supportedLngs: ['fr-FR'],
  load: 'currentOnly',
  defaultNS,
  cleanCode: true,
  resources
});

export default i18next;

My resources constante is here :

import { fr } from './fr-FR';

export const resources = {
  'fr-FR': fr
};

and my fr constante :

import dashboardPage from './dashboard.page.json';
import formValidation from './form.validation.json';
import profilPage from './profil.page.json';
import trajectPage from './traject.page.json';
import welcomePage from './welcome.page.json';
import signPage from './sign.page.json';
import homePage from './home.page.json';
import notifications from './notifications.json';
import feedbackPage from './feedback.page.json';
import common from './common.json';

export const fr = {
  common,
  'dashboard.page': dashboardPage,
  'form.validation': formValidation,
  'sign.page': signPage,
  'profil.page': profilPage,
  'traject.page': trajectPage,
  'welcome.page': welcomePage,
  'home.page': homePage,
  'feedback.page': feedbackPage,
  notifications
};

And as example, common.json :

{
    "greetings": {
        "goodMorning": "Bonjour,",
        "goodAfternoon": "Bonsoir,"
    },
    "actions": {
        "startTracking": "Démarrer",
        "stopTracking": "Arrêter"
    }
}

Is my code is right ? Maybe the error is here ?

stale[bot] commented 3 years ago

This issue has been automatically marked as stale because it has not had recent activity. It will be closed if no further activity occurs. Thank you for your contributions.