react-i18next icon indicating copy to clipboard operation
react-i18next copied to clipboard

Typescript error when using variables in Trans component

Open eszthoff opened this issue 3 years ago • 21 comments

🐛 Bug Report

Typescript throws when using variable in Trans component. Maybe a bit of an edge case, but when using a custom component that expects string as children within Trans and the child to this component should include a variable Typescript throws an error. Also it is impossible to ignore TS in this situation.

To Reproduce

A codesandbox

Note that this is a minimal reproduction (e.g. i18next is not fully set up).

One of the conditions for this bug is that you have the child in a separate line. If they would be in one line, you could add a `{/* @ts-ignore */} before hand. However it is not working when the code is in multiple lines.

Expected behavior

TS doesn't throw or at least there is a way to ignore it.

eszthoff avatar May 23 '22 11:05 eszthoff

outside of your StringWrapper it works, right? https://codesandbox.io/s/mutable-bash-6gdqy2?file=/src/App.tsx:323-348

adrai avatar May 23 '22 11:05 adrai

@adrai do you mean if you move the variable to a different position? Yes, that works. But that is not a viable solution, if the variable needs to be inside the string of StringWrapper.

eszthoff avatar May 23 '22 11:05 eszthoff

I'm not a TypeScript user, but if it works like this:

      <Trans>
        <StringWrapper className="my-class">
          Your count 
        </StringWrapper>
        {{ amount: data.length }} has been counted
      </Trans>

it may be your StringWrapper component is doing something that is causing the problem.

adrai avatar May 23 '22 11:05 adrai

This solution doesn't answer the requirements (see above). Also that component doesn't do much (see in the example attached).

eszthoff avatar May 23 '22 12:05 eszthoff

Hey @eszthoff, that's happening because your StringWrapper doesn't accept ReactNode just string. You can simply remove the children prop from StringWrapper, and it should work fine, React.FC already includes a children.

pedrodurek avatar May 24 '22 17:05 pedrodurek

Thank for your work and the suggestions.

Unfortunately changing the children type will also not fulfil the requirements. Even in the current (contrived) example that will allow incorrect children to be passed. E.g. <div>text</div> that would result in incorrect HTML such as <span><div>text</div></span>.

I think there are several real world scenarios when you want to have a strong type checking:

  • StringWrapper is part of a library that you expect others to use
  • StingWrapper is used by a large amount of developers in your company in different context where Trans is only one use case.

eszthoff avatar May 25 '22 14:05 eszthoff

I played around a bit more with the problem and found a solution that I think is acceptable. Posting it here, in case it helps others. The solution is not perfect, but at least it allows one to add a @ts-ignore comment.

If the TS declaration changed from using React.FC to just declaring the props, it moves to error message one line higher, hence allowing adding comments to the line above that. The resulting code would look something like that:

const StringWrapper = (props: Props) => {...}

If needed the return type can be also added in the usual way.

eszthoff avatar May 25 '22 14:05 eszthoff

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[bot] avatar Jun 04 '22 01:06 stale[bot]

I'm experiencing something similar:

const title = `${title} something`
return (
    <Trans i18nKey="myKey">
        Edit <span style={{color: '#40a9ff'}}>{{title}}</span>
    </Trans>
)
// where myKey=Edit Entity<1>{{title}}</1>

yields: TS2322: Type '{ title: string; }' is not assignable to type 'ReactNode'.   Object literal may only specify known properties, and 'title' does not exist in type 'ReactElement<any, string | JSXElementConstructor<any>> | ReactFragment | ReactPortal'.

ChambreNoire avatar Jun 05 '22 18:06 ChambreNoire

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[bot] avatar Jun 12 '22 23:06 stale[bot]

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[bot] avatar Jun 27 '22 22:06 stale[bot]

Problem still exists. Maybe there's a workaround for default components like span?

TrueCarry avatar Jul 06 '22 11:07 TrueCarry

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[bot] avatar Jul 13 '22 20:07 stale[bot]

Problem still exists...

rodriguezmarting avatar Jul 20 '22 21:07 rodriguezmarting

Everyone who is still facing this problem, please set allowObjectInHTMLChildren to true in your react-i18next type declaration file. You can follow the instructions here to learn how to create a type declaration file, it should look similar to this one:

import "react-i18next";
...

declare module "react-i18next" {
  interface CustomTypeOptions {
    allowObjectInHTMLChildren: true, // This property should be true
    defaultNS: "ns1";
    resources: {
      ns1: typeof ...;
    };
  }
}

If the error still persists, please share a minimalistic version of your app (using the Trans component), and I'll help you.

pedrodurek avatar Jul 21 '22 00:07 pedrodurek

As far as I could see, @eszthoff issue is different from others as she can't change the children Props, if you can't change the children props to React.ReactNode or string | Record<string, unknown>;, please explain why and I'll try to help you.

pedrodurek avatar Jul 21 '22 00:07 pedrodurek

@pedrodurek Setting allowObjectInHTMLChildren to true works for me. Thanks.

What is the reason why it is not true by default? Are there any disadvantages?

falkoschumann avatar Jul 21 '22 06:07 falkoschumann

@pedrodurek Setting allowObjectInHTMLChildren to true works for me. Thanks.

What is the reason why it is not true by default? Are there any disadvantages?

Wouldn't that suppress typescript errors on any children prop? Objects are not valid JSX children, meaning that outside Trans components I'd expect it to crash the runtime?

BrooceLR avatar Jul 21 '22 13:07 BrooceLR

@BrooceLR I tested this, allowObjectInHTMLChildren works like disabling the TypeScript warning globally. It suppresses warnings in all JSX. So this is not a good solution.

falkoschumann avatar Jul 21 '22 13:07 falkoschumann

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[bot] avatar Aug 11 '22 23:08 stale[bot]

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[bot] avatar Sep 20 '22 19:09 stale[bot]

I found an solution for this, you can pass values to Trans component like this:

      <Trans values={{ amount: data.length }}>
        <StringWrapper className="my-class">
          Your count 
        </StringWrapper>
        {data.length} has been counted
      </Trans>

radekskrabucha avatar Jan 04 '23 16:01 radekskrabucha