Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

withTranslation - Type error: Type 'TFunctionResult' is not assignable to type 'ReactNode' #1559

Closed
MarcelGeo opened this issue Oct 7, 2022 · 43 comments · Fixed by i18next/i18next#1865
Assignees

Comments

@MarcelGeo
Copy link

Type error: Type 'TFunctionResult' is not assignable to type 'ReactNode'

This typescript error is not fixed by using withTranslation HOC.

@stale
Copy link

stale bot commented Oct 15, 2022

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.

@stale stale bot added the stale label Oct 15, 2022
@manwaring
Copy link

I'm getting the same error in the v12 release for the t function from useTranslation hook

@stale stale bot removed the stale label Oct 20, 2022
@denizguzel
Copy link

Similar error here with the v12 release,

Type 'DefaultTFuncReturn' is not assignable to type 'ReactNode'.

@adrai
Copy link
Member

adrai commented Oct 20, 2022

fyi: i18next/i18next#1852

@meyfa
Copy link

meyfa commented Oct 20, 2022

Similar error here with the v12 release

See https://www.i18next.com/overview/typescript for how to add the required type definitions. This will make the error go away. But more importantly, i18next will now ensure you don't pass arbitrary strings to the t function that are not part of your translations. The feature was implemented here: i18next/i18next#1775

By the way, thanks to everyone who made this happen! This is awesome! Finally we can statically ensure correct usage of the t function 🎉

@SergioTx
Copy link

SergioTx commented Oct 21, 2022

And what if you need to pass "arbitrary strings" or at least strings formed by concatenation?

Something like:

function component(props: {stringProp: string}) {
  const {t} = useTranslation(myNs);
  return <div>{t(props.stringProp)}</div>
}

To strictly type all strings with templates is not always an option.

Thank you!

@meyfa
Copy link

meyfa commented Oct 21, 2022

@SergioTx I think it makes sense to refactor such that the props are already translated. If this is not an option, you could use as any to evade type checking. Having the any type assertion there would at least clearly indicate to any future reader that the code is not actually safe.

@florianbepunkt
Copy link

Is there any way to disable type-checking here? In our translation workflow, we define the keys in code first with a default value... and then the translation keys are extracted by and automated process. So we have code-first approach when it comes to the keys, while the typescript docs effectively describe a translation-first approach, where the translation keys already exists in a JSON file.

@adrai
Copy link
Member

adrai commented Oct 26, 2022

Is there any way to disable type-checking here? In our translation workflow, we define the keys in code first with a default value... and then the translation keys are extracted by and automated process. So we have code-first approach when it comes to the keys, while the typescript docs effectively describe a translation-first approach, where the translation keys already exists in a JSON file.

i18next/i18next#1853 (comment)

@chybisov
Copy link

The same error with the t from useTranslation hook.

Type 'DefaultTFuncReturn' is not assignable to type 'ReactNode'.

@adrai
Copy link
Member

adrai commented Oct 27, 2022

can you try with i18next v22.0.3?

@karesztrk
Copy link

can you try with i18next v22.0.3?

It works now. No compilation errors.

@adrai
Copy link
Member

adrai commented Oct 27, 2022

@MarcelGeo can we close this?

@kirill-linnik
Copy link

I have the same problem with 22.0.4:
TS2322: Type 'DefaultTFuncReturn' is not assignable to type 'string | ReactElement<any, string | JSXElementConstructor<any>> | undefined'.
as a workaround, it looks like I should wrap all function invocation into <></>, which is a bit painful for a bit project. is there any other way fixing it?

@adrai
Copy link
Member

adrai commented Oct 29, 2022

I have the same problem with 22.0.4:

TS2322: Type 'DefaultTFuncReturn' is not assignable to type 'string | ReactElement<any, string | JSXElementConstructor<any>> | undefined'.

as a workaround, it looks like I should wrap all function invocation into <></>, which is a bit painful for a bit project. is there any other way fixing it?

Can you create a reproducible example an open a new issue?

@kirill-linnik
Copy link

kirill-linnik commented Oct 29, 2022

I have the same problem with 22.0.4:
TS2322: Type 'DefaultTFuncReturn' is not assignable to type 'string | ReactElement<any, string | JSXElementConstructor<any>> | undefined'.
as a workaround, it looks like I should wrap all function invocation into <></>, which is a bit painful for a bit project. is there any other way fixing it?

Can you create a reproducible example an open a new issue?

hmm, now I'm a bit puzzled. it works nice in a sandbox:
image
but in my project (I even deleted yarn.lock and node_modules to have it "all fresh"), I get totally different definition of t:
image

"i18next": "22.0.4",
"i18next-browser-languagedetector": "7.0.0",
"react-i18next": "12.0.0",

I'm puzzled a bit. any idea why could it be the case?

@kirill-linnik
Copy link

ok, screw sandbox, I made a demo on top of playground project: #1574

@pedrodurek
Copy link
Member

Hey @kirill-linnik, it seems message don't accept null, but t function could return null. I'd advice to fallback to something else. E.g.: t('key') ?? ''

@badsyntax
Copy link

badsyntax commented Nov 5, 2022

I'm seeing something similar. As as temporary workaround I've been able to "fix" this by overriding the TFunction interface in my i18next.d.ts file, to apply the following change:

- TDefaultResult extends DefaultTFuncReturn = string,
+ TDefaultResult extends string = string, 
In file `i18next.d.ts`:
import 'i18next';

declare module 'i18next' {
  interface TFunction<
    N extends Namespace = DefaultNamespace,
    TKPrefix = undefined
  > {
    <
      TKeys extends TFuncKey<N, TKPrefix> | TemplateStringsArray extends infer A
        ? A
        : never,
      TDefaultResult extends string = string,
      TInterpolationMap extends object = StringMap
    >(
      key: TKeys | TKeys[]
    ): TFuncReturn<N, TKeys, TDefaultResult, TKPrefix>;
    <
      TKeys extends TFuncKey<N, TKPrefix> | TemplateStringsArray extends infer A
        ? A
        : never,
      TDefaultResult extends stringWithObject = object,
      TInterpolationMap extends object = StringMap
    >(
      key: TKeys | TKeys[],
      options?: TOptions<TInterpolationMap> & {
        returnDetails: true;
        returnObjects: true;
      }
    ): TFunctionDetailedResult<TFuncReturn<N, TKeys, TDefaultResult, TKPrefix>>;
    <
      TKeys extends TFuncKey<N, TKPrefix> | TemplateStringsArray extends infer A
        ? A
        : never,
      TDefaultResult extends string = string,
      TInterpolationMap extends object = StringMap
    >(
      key: TKeys | TKeys[],
      options?: TOptions<TInterpolationMap> & { returnDetails: true }
    ): TFunctionDetailedResult<TFuncReturn<N, TKeys, TDefaultResult, TKPrefix>>;
    <
      TKeys extends TFuncKey<N, TKPrefix> | TemplateStringsArray extends infer A
        ? A
        : never,
      TDefaultResult extends stringWithObject = object,
      TInterpolationMap extends object = StringMap
    >(
      key: TKeys | TKeys[],
      options?: TOptions<TInterpolationMap> & { returnObjects: true }
    ): TFuncReturn<N, TKeys, TDefaultResult, TKPrefix>;
    <
      TKeys extends TFuncKey<N, TKPrefix> | TemplateStringsArray extends infer A
        ? A
        : never,
      TDefaultResult extends string = string,
      TInterpolationMap extends object = StringMap
    >(
      key: TKeys | TKeys[],
      options?: TOptions<TInterpolationMap>
    ): TFuncReturn<N, TKeys, TDefaultResult, TKPrefix>;
    <
      TKeys extends TFuncKey<N, TKPrefix> | TemplateStringsArray extends infer A
        ? A
        : never,
      TDefaultResult extends string = string,
      TInterpolationMap extends object = StringMap
    >(
      key: TKeys | TKeys[],
      defaultValue?: string,
      options?: TOptions<TInterpolationMap> | string
    ): TFuncReturn<N, TKeys, TDefaultResult, TKPrefix>;
  }
}
"i18next": "22.0.4",
"react-i18next": "12.0.0",

@kirill-linnik
Copy link

@badsyntax yeah, there are several ways to workaround it. I've decided to postpone the update until it is fixed properly

@pedrodurek seems like we need t to return string ;)

@pedrodurek
Copy link
Member

If you have access to the translation files in build time and follow the instructions here, t function will always return the correct type (string).
Please take a look at our examples: https://github.com/i18next/react-i18next/tree/master/example/react-typescript, https://github.com/i18next/i18next/tree/master/examples/typescript

@is-jonreeves
Copy link

@pedrodurek we have also ran into this same issue while upgrading a few of our projects. For now I'm working around this with the following (based on the comment here):

types/react-i18next.d.ts

import 'react-i18next';

declare module 'i18next' {
  interface CustomTypeOptions {
    defaultNS: 'common';
    resources: {
      [key: string]: {
        [key: string]: string;
      };
    };
  }
}

We found that doing what the docs suggested and importing all of our namespace/JSON files resulted in a painfully slow VSCode. Further more, our projects use i18nHttpBackend to load the translations at runtime, so having to import/hardcode these into the project would unnecessarily bloat our resulting bundle and defeat the purposes of async loading the translations.

For what its worth I was able to import these into the d.ts file instead and I suspect it might not affect the bundle that way, however as I said the typechecking really slowed down.

import common from '@/../public/static/locales/en/common.json';
import home from '@/../public/static/locales/en/home.json';
...

declare module 'i18next' {
  interface CustomTypeOptions {
    defaultNS: 'common';
    resources: {
      common: typeof common;
      home: typeof home;
      ...
    };
  }
}

While the IDE prompts were nice (aside from the slowdown), I did run into a few other problems:

  1. The DefaultNS doesn't appear to be taken into account and type errors appear, e.g...
// common.json
{
  "errors": {
    "unexpected": "Unexpected error"
  }
}

// home.json
{
  "page": {
    "title": "Welcome"
  }
}
// Relying on DefaultNS (common)
const { t } = useTranslation('home', { useSuspense: true });

console.log(, t('page.title'))
console.log(, t('common:errors.unexpected'))
// Explicitly including "common"
const { t } = useTranslation([ 'common', 'home' ], { useSuspense: true });

console.log(, t('home:page.title'))
console.log(, t('common:errors.unexpected'))
  1. We weren't able to use dynamic strings any more:
console.log(, t('common:dates.days.mon'))
console.log(, t(`common:dates.days.${day}`))

With all of this in mind, and realizing my usecases may be the minority, it would still be great to see a way to bypass/disable the typing requirements as an option. For now the workaround mentioned above will work for me. Hope its helpful to others.

@pedrodurek
Copy link
Member

pedrodurek commented Nov 11, 2022

Hey @is-jonreeves.

// Relying on DefaultNS (common)
const { t } = useTranslation('home', { useSuspense: true });

console.log(, t('page.title'))
console.log(, t('common:errors.unexpected'))

This pattern ☝️ is not supported due to compilation time constrains, I'd advice to explicitly include the "common" namespace. I'm working on some improvements that will allow us to do that, but it may require a lot of tests and benchmark.

console.log(, t('common:dates.days.mon'))
console.log(, t(`common:dates.days.${day}`))

day here needs to be a literal value, not only a string, otherwise it won't be type-safe. For example:

const day = 'today' as const;
// type inferred: 'today' instead of string

console.log(, t(`common:dates.days.${day}`))

with union type:

type Props = {
  day: 'today' | 'tomorrow' | 'yesterday'
}
const Component = ({day}: Props) => {
  console.log(, t(`common:dates.days.${day}`))
}

@adrai
Copy link
Member

adrai commented Nov 12, 2022

should be fixed with i18next v22.0.5

@eiskalteschatten
Copy link

eiskalteschatten commented Nov 13, 2022

v22.0.5 didn't fix it for me unfortunately. I am still getting the following error and had to downgrade to 22.0.3:

Type 'DefaultTFuncReturn' is not assignable to type 'string | undefined'.

I'm using the following with Node 18.12.1:

"i18next": "^22.0.5",
"react-i18next": "^12.0.0",
"typescript": "^4.8.4"

And if it helps, this is the project: https://github.com/eiskalteschatten/InOrdnung

@manuelpoelzl
Copy link

v22.0.5 didn't fix it for me unfortunately. I am still getting the following error and had to downgrade to 22.0.3:

Type 'DefaultTFuncReturn' is not assignable to type 'string | undefined'.

I'm using the following with Node 18.12.1:

"i18next": "^22.0.5",
"react-i18next": "^12.0.0",
"typescript": "^4.8.4"

And if it helps, this is the project: https://github.com/eiskalteschatten/InOrdnung

Have the exact same problem.

@pedrodurek
Copy link
Member

t function can return null, this behaviour is set by default, if you want to change it, set returnNull type to false.

// i18next.d.ts
import 'i18next';

declare module 'i18next' {
  interface CustomTypeOptions {
    returnNull: false;
    ...
  }
}

I also recommend updating your i18next configuration to behave accordantly. https://www.i18next.com/overview/configuration-options#translation-defaults

i18next.init({
  returnNull: false,
   ...
});

@schankam
Copy link

t function can return null, this behaviour is set by default, if you want to change it, set returnNull type to false.

// i18next.d.ts
import 'i18next';

declare module 'i18next' {
  interface CustomTypeOptions {
    returnNull: false;
    ...
  }
}

I also recommend updating your i18next configuration to behave accordantly. https://www.i18next.com/overview/configuration-options#translation-defaults

i18next.init({
  returnNull: false,
   ...
});

Does not seem to work for me...

    "i18next": "^22.0.5",
    "i18next-browser-languagedetector": "^7.0.1",
    "i18next-http-backend": "^2.0.1",
    "react-i18next": "^12.0.0",

@adrai
Copy link
Member

adrai commented Nov 15, 2022

@schankam make sure you define returnNull: false in the init options and in the i18next.d.ts file...
if this still does not work, please provide a minimal reproducible example

@Willham12
Copy link

in my case changing declare module 'react-i18next' --> declare module 'i18next' did the trick ;D

@imransilvake
Copy link

imransilvake commented Nov 28, 2022

doesn't work

"i18next": "^22.0.6",
"react-i18next": "^12.0.0",
// init i18n
i18n.use(initReactI18next)
	.init({
		returnNull: false,
		resources: { en, de },
		lng: 'en',
		keySeparator: '.',
		interpolation: { escapeValue: false }
	})
	.then();

Screenshot 2022-11-28 at 17 04 10

@kazhuravlev
Copy link

kazhuravlev commented Dec 13, 2022

Until the problem not fixed, we can use some sort of crutch like:

function useXTranslation() {
  const {t} = useTranslation()

  return {t: t<string>};
}

and replace const { t } = useTranslation() to const { t } = useXTranslation()

my versions of i18next

...
 "i18next": "22.4.5",
 "react-i18next": "12.1.1",
...

NOTE: possibly this is insane advice. I am playing with TS/JS for only 5 days. But this is working for my case. 🤣

@adrai
Copy link
Member

adrai commented Dec 13, 2022

Until the problem not fixed, we can use some sort of crutch like:

function useXTranslate() {

  const {t} = useTranslation()



  return {t: t<string>};

}

and replace const { t } = useTranslate() to const { t } = useXTranslate()

my versions of i18next


...

 "i18next": "22.4.5",

 "react-i18next": "12.1.1",

...

NOTE: possibly this is insane advice. I am playing with TS/JS for only 5 days. But this is working for my case. 🤣

Have you tried with i18next v22.4.5 ?

@kazhuravlev
Copy link

@adrai I think yes, because my version is 22.4.5. May this output will be more helpful:

$ npm ll | grep i18next                                                                                                                                                   
├── i18next-browser-languagedetector@7.0.1
├── i18next@22.4.5
├── react-i18next@12.1.1

This is looks like I use exactly 22.4.5

@adrai
Copy link
Member

adrai commented Dec 13, 2022

a minimal reproducible example repository would help

@kazhuravlev
Copy link

Almost all people in this thread is describe the same problem with type definitions. No new info in my case.

If you need an additional info - feel free to ask.

@adrai
Copy link
Member

adrai commented Dec 13, 2022

Almost all people in this thread is describe the same problem with type definitions. No new info in my case.

If you need an additional info - feel free to ask.

I don't see any i18next.d.ts file defining returnNull: false;: https://www.i18next.com/overview/typescript#argument-of-type-defaulttfuncreturn-is-not-assignable-to-parameter-of-type-xyz

image

@kazhuravlev
Copy link

wow. this is working. thx.

But also, I think that this functional should be working without additional manipulations around the code. Hope we will get a function that will return just a strings.

@adrai
Copy link
Member

adrai commented Dec 13, 2022

Like described, the t function is potentially returning null... but it might be in the next major version we will set the default for returnNull to false => i18next/i18next#1884

@m-nathani
Copy link

Like described, the t function is potentially returning null... but it might be in the next major version we will set the default for returnNull to false => i18next/i18next#1884

any expected date on when it could be released ? otherwise we can keep using the old version 22.0.6 ,

@adrai
Copy link
Member

adrai commented Dec 19, 2022

@m-nathani no ETA at the moment... waiting for @pedrodurek's feedback...
in the meantime set returnNull to false yourself https://www.i18next.com/overview/typescript#argument-of-type-defaulttfuncreturn-is-not-assignable-to-parameter-of-type-xyz

@m-nathani
Copy link

@m-nathani no ETA at the moment... waiting for @pedrodurek's feedback...
in the meantime set returnNull to false yourself https://www.i18next.com/overview/typescript#argument-of-type-defaulttfuncreturn-is-not-assignable-to-parameter-of-type-xyz

Sounds great.. thanks for the link 👍

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging a pull request may close this issue.