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

This JSX tag's 'children' prop expects a single child of type 'ReactI18NextChild | Iterable<ReactI18NextChild>' when using `allowObjectInHTMLChildren`

Open davehowson opened this issue 2 years ago • 6 comments

🐛 Bug Report

After upgrading to React v18 and Reacti18Next to v11.18, I had an issue with objects not being accepted inside the Trans component, something similar to this. The fix for that was to add the allowObjectInHTMLChildren property, which fix that issue, but it lead to another issue. Now, when I try to have the result of a t function along with some other strings inside a p or span component, I get the error

This JSX tag's 'children' prop expects a single child of type 'ReactI18NextChild | Iterable<ReactI18NextChild>' when using `allowObjectInHTMLChildren

To Reproduce

Codesandbox - https://codesandbox.io/s/react-i18next-type-issue-uidhq7 Please check the error on src/App.tsx:10

<p>{t("simpleContent")} with extra stuff</p>

Proposed Solution

I tried playing around with the types in the library and it seems like changing node_modules/react-i18next/ts4.1/index.d.ts:108 as follows will fix this.

declare module 'react' {
  interface HTMLAttributes<T> {
    children?: ReactI18NextChild | ReactI18NextChild[];
  }
}

The problem, at least for me, seems to be with Iterable<ReactI18NextChild> not working as expected for multiple children

davehowson avatar Aug 15 '22 08:08 davehowson

Hi @pedrodurek, any update on this? I was planning on using https://github.com/ds300/patch-package to solve this for now, unless a PR is in the works?

ghost avatar Aug 23 '22 03:08 ghost

We're also having this problem unfortunately. ☹️

eiskalteschatten avatar Aug 23 '22 12:08 eiskalteschatten

same problem also with React 17 and react-i18next (v11.18.3)

debugelton avatar Aug 26 '22 20:08 debugelton

This is probably because the type definition is React.ReactNode | Record<string, unknown>, while i18next type definition has object in the union type: https://github.com/i18next/i18next/blob/132a02461479c1b14709ff0bce1db484578ccb30/index.d.ts#L687

I think (could be wrong) that Record<string, unknown> and object are not compatible if you change i18next to React.ReactNode | object it works.

I also changed from Iterable<ReactI18NextChild> to ReactI18NextChild[]

JCMais avatar Sep 14 '22 13:09 JCMais

I'm having a similar problem with PropTypes.InferProps. If I use PropTypes.node for my children prop, and use {children} in a simple div, I get this error:

Type 'ReactNodeLike' is not assignable to type 'ReactI18NextChild | Iterable<ReactI18NextChild>'. Type '{}' is not assignable to type 'ReactI18NextChild | Iterable<ReactI18NextChild>'.

neatroapp avatar Sep 15 '22 20:09 neatroapp

Never do this in open source libs. If you want to get custom children, use custom (alias) types. Never reassign types of different lib!

image

sshmyg avatar Sep 20 '22 10:09 sshmyg

Same problem after bumping React to v18

kanonieer avatar Sep 26 '22 08:09 kanonieer

Same

rjaguilar avatar Sep 26 '22 20:09 rjaguilar

Same issue here

davidpp avatar Sep 26 '22 21:09 davidpp

same here

estrelasteinkirch avatar Sep 27 '22 19:09 estrelasteinkirch

same issue here

cuttleman avatar Oct 07 '22 08:10 cuttleman

same issue if you have something like <span>{props.somePassedInReactNodeOrReactElement}</span>

Type 'ReactNode' is not assignable to type ReactI18NextChild | Iterable<ReactI18NextChild>.

rene-stesl avatar Oct 12 '22 14:10 rene-stesl

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 Oct 20 '22 02:10 stale[bot]

Same issue here

renomateo avatar Oct 20 '22 02:10 renomateo

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 Oct 29 '22 07:10 stale[bot]

Same issue

sklawren avatar Oct 31 '22 03:10 sklawren

This is a big issue relative to typingm due to redefining children of react module. This is not a good practise, as it breaks other typings.

This JSX tag's 'children' prop expects a single child of type 'ReactI18NextChild | Iterable<ReactI18NextChild>', but multiple children were provided.
but multiple children were provided.

sergiohgz avatar Nov 10 '22 12:11 sergiohgz

Is that really the solution to just ignore the growing number of Typescript Users? Why has this type been overridden in the first place? Just leave react typings alone, please! Does it really need that override? What is it for?

rene-stesl avatar Nov 10 '22 14:11 rene-stesl

@rene-stesl idk...but this module is far from ignoring typescript users. Just go through the changelog https://github.com/i18next/react-i18next/blob/master/CHANGELOG.md and check how many releases are just related to work for typescript users.

@pedrodurek puts a lot of his own (free) time into improving the typings and does an awesome job for the community.

jamuhl avatar Nov 10 '22 16:11 jamuhl

Thanks for backing me up @jamuhl, I appreciate it!

@rene-stesl, the types here work perfectly, sometimes it's just hard to please everyone, but I'm doing my best here to address the issues whenever I have free time. When it comes to complex types like the i18next ones, where we recursively go through all translations to infer the appropriate keys and return type, we need to be careful to address some issues due to compilation time concern, so there is a lot of effort on testing and benchmarking it.

pedrodurek avatar Nov 10 '22 23:11 pedrodurek

@jamuhl, @pedrodurek I know that it is not easy to sacrifice your free time for such a project and only get criticism. This is definitely not my intention.

But IMHO this ticket gets ignored since it was opened on Aug 15. I personally don't think the Types work perfectly in this case because if you just add react-i18next as dependency to an existing project it is possible that the build fails! And I personally think that just adding a dependency to a project shouldn't be able to do that!

If someone can explain why the React Types were overwritten, it might be easier to find a solution.

rene-stesl avatar Nov 11 '22 07:11 rene-stesl

@rene-stesl

I guess (not a typescript user):

  • react team decided to make types stricter not allowing objects inside the react-element children array (as it makes no sense to their use case -> you can't render objects)

in react-i18next we decided long before that objects are a good way to pass in interpolation values and keeping i18next translation syntax <Trans>Some interpolation by passing in an object {{theObject}}</Trans>. That is absolute valid in javascript.

So the solution is either to extend the types or not using objects inside react-elements.

(Like said - i don't use typescript - so I just believe that this was the reason)

jamuhl avatar Nov 11 '22 07:11 jamuhl

@pedrodurek I'd love to hear your thoughts on this problem since you are clearly a Typescript user. I think most people on this thread use typescript and I would hope that if the real crux of the problem was clarified, then maybe someone might find an elegant solution that makes your life easy.

stee-ren avatar Nov 11 '22 08:11 stee-ren

Ok so I just quickly went over the the said typings. I would love to take a deeper look but I have absolutely no time for this.

As far as I can see the react typing for the DOMAttributes look like this (and it seems like this was even the case with react 16, so no changes there)

interface DOMAttributes<T> {
    children?: ReactNode | undefined;
    ...

The override of react-i18next

type ObjectOrNever = TypeOptions['allowObjectInHTMLChildren'] extends true
  ? Record<string, unknown>
  : never;
type ReactI18NextChild = React.ReactNode | ObjectOrNever;

declare module 'react' {
  interface HTMLAttributes<T> {
    children?: ReactI18NextChild | Iterable<ReactI18NextChild>;
  }
}

I'm absolutely no Typescript expert but the proposed solution by @davehowson is working and would be my suggested quick fix as well. I guess that the problem lies in the use of the Iterable interface, since any Object could be iterable if it implements the interface. So my best bet is that the Typescript interpreter just want's to tell us that our provided Object doesn't implement the Iterable interface and therefore throws this error. With the use of ReactI18NextChild[] however, we tell Typescript that it has to be an Array. So Typescript knows that the Object applies to ReactI18NextChild instead of Iterable<ReactI18NextChild>. But as said I'm really no expert, I'm just guessing 🤣😉😅.

The big question is since react 16 used the same typing for the children, why is this "hack" needed now?

rene-stesl avatar Nov 11 '22 09:11 rene-stesl

Just a workaround: serve your children inside fragment <></>.

AkiraVoid avatar Nov 17 '22 09:11 AkiraVoid

Hi, did you try to add allowObjectInHTMLChildren: true and change the declare module 'react-i18next' by declare module 'i18next' in react-i18next.d.ts ?

import 'react-i18next'

declare module 'i18next' { // change from react-i18next to i18next
	interface CustomTypeOptions {
		returnNull: false
		allowObjectInHTMLChildren: true // add this solve my issue
	}
}

I have react-i18next v12, typescript v4.9 and react v18.2

I think https://github.com/i18next/i18next/issues/1859 is a duplicate of this issue

wiinxt avatar Nov 17 '22 18:11 wiinxt

Same issue here! type '{ lng: ReactNode; }' is not assignable to type 'ReactI18NextChild | Iterable<ReactI18NextChild>'.

Object literal may only specify known properties, and 'lng' does not exist in type 'ReactElement<any, string | JSXElementConstructor> | ReactFragment | ReactPortal | Iterable<ReactI18NextChild>'.ts(2322) index.d.ts(46, 5):

The expected type comes from property 'children' which is declared here on type 'DetailedHTMLProps<HTMLAttributes<HTMLElement>, HTMLElement>'

DSamniashvili avatar Dec 11 '22 22:12 DSamniashvili

out of curiosity... check with i18next v22.4.5

adrai avatar Dec 13 '22 07:12 adrai

Tried it with v22.4.6, same issue:

TS2322: Type 'ReactNode | ((data: DataNode) => ReactNode)' is not assignable to type 'ReactI18NextChild | Iterable<ReactI18NextChild>'.   Type '(data: DataNode) => ReactNode' is not assignable to type 'ReactI18NextChild | Iterable<ReactI18NextChild>'.

rene-stesl avatar Dec 20 '22 08:12 rene-stesl

Even tried to update react-i18next from 11.18.6 to 12.1.1, but then i get a big pile of typescript errors. e.g:

  • TS2746: This JSX tag's 'children' prop expects a single child of type 'ReactNode', but multiple children were provided
  • TS2322: Type 'ReactI18NextChild | Iterable<ReactI18NextChild>' is not assignable to type 'ReactNode'.   Type 'Record<string, unknown>' is not assignable to type 'ReactNode'.     Type 'Record<string, unknown>' is missing the following properties from type 'ReactPortal': key, children, type, props

rene-stesl avatar Dec 20 '22 08:12 rene-stesl