Search code examples
reactjstypescripti18nexthigher-order-componentsreact-i18next

Using higher order component in Typescript


I am trying to use react-i18next with Typescript, and I am running into a few issues.

Here's my interface for the props that I am expecting.

import * as i18n from 'i18next';

export interface ReactI18nProps {
  t: i18n.TFunction;
}

I then have a navigation component, which I compose.

import { translate } from 'react-i18next';

function Navigation({ t }: ReactI18nProps) {
  const userProfile = JSON.parse(localStorage.getItem('profile') || '');
  return (
    ...
  );
}

export default translate('navigation')(Navigation);

And now I simply want to render this component that I have composed

function Appliances({ location }: NavLinkProps) {
  const userId = location && location.state.userId;
  return (
    <div>
      <Navigation />
    </div>
  );
}

However, at this juncture, TS fails me and I get the following error

Type '{}' is not assignable to type 'IntrinsicAttributes & ReactI18nProps'.
  Type '{}' is not assignable to type 'ReactI18nProps'.
    Property 't' is missing in type '{}'.

Could someone please explain what I am doing incorrectly.


Solution

  • As the error message suggests:

    Property 't' is missing in type '{}'

    Your component expects the props to be:

    interface ReactI18nProps {
        t: i18n.TFunction;
    }
    

    But you're not passing this t:

    <Navigation />
    

    You can either make this t property optional:

    interface ReactI18nProps {
        t?: i18n.TFunction;
    }
    

    Or satisfy it:

    <Navigation t={ (key: string, options: i18n.TOptions) => return getTranslation(key, options) } />
    

    Edit

    Based on the type definitions for react-i18next translation function:

    function translate<TKey extends string = string>
        (namespaces?: TKey[] | TKey, options?: TOptions)
            : <C extends Function>(WrappedComponent: C) => C;
    

    The type of the component you pass is the exact type you get back, meaning that the same props type is used.
    In this case, you'll need to use an optional t because sometimes it is used without this property (when you call it) but then it is used with t (when the translate method calls it).

    To avoid the compiler complaining about t possibly be undefined you can use the non-null assertion operator:

    function Navigation({ t }: ReactI18nProps) {
        ...
        const translation = t!(...);
        ...
    }
    

    Notice that it's using t! instead of just t.