`<Trans>` typing does not support interpolation deeper than first-level children
🐛 Bug Report
The following code raises a Typescript error:
<Trans>
<p>Ticket type “{{ticketType}}” not supported</p>
</Trans>
The error is:
Object literal may only specify known properties, and 'ticketType' does not exist in type 'ReactElement<any, string | JSXElementConstructor<any>> | Iterable<ReactNode> | ReactPortal'.
The following code does not raise the error:
<Trans>
Ticket type “{{ticketType}}” not supported
</Trans>
To Reproduce
<Trans>
<p>Ticket type “{{ticketType}}” not supported</p>
</Trans>
with react-i18next = 15.5.2, i18next = 24.2.3, typescript = 5.8.3, @types/react = 18.2.48
Expected behavior
No error should be raised.
Your Environment
- runtime version: node v20.18
- i18next version: 24.2.3
- react-i18next version: 15.5.2
- typescript version: 5.8.3
- @types/react version: 18.2.48
- os: Mac
That's a TypeScript limitation...
Try one of the alternatives:
<p>
<Trans>
Ticket type “{{ticketType}}” not supported
</Trans>
</p>
<Trans
defaults="<0>Ticket type “{{ticketType}}” not supported</0>"
values={{ ticketType }}
components={[<p></p>]}
/>
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.
I stumbled accross this issue only after updating from i18next 24 to i18next 25.
I don't think this is really a ts limitation, as There's even a type merging option specificially for this:
CustomTypeOptions has the option allowObjectInHTMLChildren: true
I noticed that after updating and even though I have set this option to true,ObjectOrNever suddenly resolved to never.
I did some investigating and I am pretty sure this is just due to unexpected dependency resolving.
Essentially, since the react-i18next package defines "i18next": ">= 23.2.3", in its peer dependencies, this was allready fulfilled by v24 and the package-lock files makes it so that this version will persist for react-i18next, essentially leading to a setup, where we had both v24 and v25 installed (in different folders). Now the type merging doesn't work anymore since it applies it to a different instance of the types (it imports from its own version of i18next rather than the one used by the app).
Essentially npm was trying to be to smart for its own good.
Too complicated? Don't worry.
Here's how we were able to fix this issue:
-
npm uninstall i18next -
npm uninstall react-i18next - Uninstall any other dependencies, that might have a dependency on
i18next. In our case this was onlyi18next-parser. - Search your package-lock.json for
"i18next". You should not find anything. Equally it should not exist in your node_modules. If it does, investigate further to see what might be causing it to be installed. - Time to reinstall! i18next first:
npm install i18next@latest -
npm install react-i18next - reinstall all other packages you uninstalled in step 3.
Following these steps will clean up your package-lock.json and cause react-i18next to understand that it should use the 25 version as well. This solved the issue for us and now everything compiles fine with i18next v25.
Might be worth a shot to try by anybody else experiencing this issue?
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.
@janpaepke thanks! To complete your instructions:
- first make sure to follow https://www.i18next.com/overview/typescript#custom-type-options to create a
i18next.d.tsfile and enableallowObjectInHTMLChildren - then, as @janpaepke said, make sure only one version of i18next is in your package.json / yarn.lock (follow dependents up the tree)
In our case, we had a direct dependency to i18next-parser which has an outdated dependency range for i18next. We had to force resolution to v25 (using the resolutions dict in package.json) to completely remove v24.
I've opened https://github.com/i18next/i18next-parser/issues/1144 to fix the dependency range.
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.