-
-
Notifications
You must be signed in to change notification settings - Fork 1k
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
Cannot pass objects to <Trans>
with React 18 type definitions
#1483
Comments
@high1 maybe related? |
@adrai - React did remove implicit children on their components in @types/react18, as @LukasKalbertodt aleady said, so I would expect something like this to happen - if this is children related, Trans component just needs to allow children explicitly. |
I took a look at this today. @LukasKalbertodt is 100% on the point - another breaking change in React 18 typings is that objects are removed from ReactNode. And the suggested solution should work - but if React.ReactNode | Record<string, unknown> gets added to definitions, another null-undefined union error happens. I would disable this rule at this point, if that's fine, @adrai - and update tslint to it's latest version, 6.1.3. And then I would suggest that the repo upgrades to @typescript-eslint. |
need @pedrodurek's feedback here 🙏 |
This is not yet fixed. The code in the top level comment now (11.16.7) results in:
The linked PR allowed passing one object as children, but not a list of different objects mixed with strings or nodes. All of these use cases were "supported" via For the first error, I think this should work: type TransChild = React.ReactNode | Record<string, unknown>;
export type TransProps<...> = {
children?: TransChild | TransChild[];
...
}; However, this doesn't fix the second error. And I'm not sure we can fix it? Here it's about the |
This came with a few breaking changes. See this for the full explanation: DefinitelyTyped/DefinitelyTyped#56210 The most impactful for us is that `React.FC` does not have implicit `children` anymore. So all components that use children, have to declare them explicitly. This is a lot of changes, but a net win for type safety. Also, due to some changes, `TResult` (what `t()` returns) was not assignable to `ReactNode`. This was fixed in `react-i18next`. Thus, that package is also updated. Further, `{}` was removed from `ReactNode`, which made some `Trans` usages not compile anymore. I opened this issue: i18next/react-i18next#1483 This is not completely fixed yet, but by just changing one line, we can make it compile already.
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. |
This is still a problem. Please don't close. |
@pedrodurek Do you have some feedback? |
I'll be investigating this one today |
Sorry for the delay guys! <Trans i18nKey="userMessagesUnread" count={count}>
Hello <strong>{{name}}</strong>
</Trans> So That's not something that we can't control in "theory"... We can always use type augmentation to change the Anyway, I created a draft PR #1492 that solves it. If you accept the condition above, we can merge it. I want you to hear your thoughts first! |
To be clear: Objects were never accepted by the runtime. You would've seen an error at runtime. Objects were unintentionally accepted by the types which we fixed in the React 18 types.
Note that this would affect every code in an application if they import from your library. Since accepting objects is unsound, I would advise against this. Maybe you're using a different JSX factory that somehow transforms these objects? In this case I don't have a good solution. |
That's my biggest concern.
Hey @adrai, what's your thoughts? |
Sorry, I've no idea... I'm not a TypeScript user... maybe html-parse-stringify => https://github.com/i18next/react-i18next/blob/master/src/Trans.js#L136 is influencing this? |
|
It's not a runtime issue...as (not like typescript) javascript is just code ;)...react's JSX is nothing but a stack of function calls...the JSX element's children is just an array of objects. So we take that That way we can use i18next JSON interpolation syntax inside JSX elements...it's somewhat clever but has its drawbacks with over-restrictive typescript...if it's good or bad thing might depend on the user. If typescript users prefer not extending this...the other option is to not allow using Trans in that way -> but use it like for ICU syntax https://react.i18next.com/misc/using-with-icu-format#using-the-trans-component |
Alternatively, we can add a new property |
Is this something that could work for you @LukasKalbertodt and other TypeScript users? |
@high1 @enoh-barbu what's your opinion? |
I think there is no other simpler solution to this without chaning the current API. Passing object to JSX is not a common procedure indeed, but in this context we can live with this as it's simpler to read the code and understand what's the intention of that object. Otherwise a change in the API will be needed, eventaully passing more props to tl;dr : yes, I'm fine with extra |
@nickclyde what about you? |
v11.16.8 offers allowObjectInHTMLChildren option |
Came across this issue as well, here is how I resolved it:
With the translation using the same <1></1> and <3></3>. |
For anyone stumbling across this in the future and needs to figure out how to use the
|
Using the suggested .d.ts mod from @dave-multiplier breaks all my other JSX tags with errors like: error TS2746: This JSX tag's 'children' prop expects a single child of type 'ReactI18NextChild | Iterable<React
I18NextChild>', but multiple children were provided. The above error is thrown for elements like: <label htmlFor={`${id}-phone`}>
{t('Phone Number')}
<RequiredMark />
</label> Wrapping |
Apologies for that @ripvannwinkler |
App.tsx:28のエラーは↓をちゃんと読まないといけないけど読んでない。動いてはいる。 i18next/react-i18next#1483
In case it helps anyone else: Using <Trans i18nKey="userMessagesUnread" count={count}>
Hello <strong title={t('nameTitle')}>{{name} as any}</strong>, you have {{count}} unread message.
</Trans> I use a type alias to make this more self-documenting: /**
* Interpolation for `t` - see https://github.com/i18next/react-i18next/issues/1483
*/
export type TI = any;
// ...
return (
<Trans i18nKey="userMessagesUnread" count={count}>
Hello <strong title={t('nameTitle')}>{{name} as TI}</strong>, you have {{count}} unread message.
</Trans>
); (This relies on new work in i18next-parser; see i18next/i18next-parser#603.) |
This issue still exists even with |
Upgraded to |
For anyone landing here today, this worked fine for me: <Trans
t={t}
values={{ minutesToLogout, secondsToLogout }}
components={[<span className={minutesToLogout === 0 ? styles.danger : ''} key="dummy-key" />]}
>
{'You will be signed out in <0>{{ minutesToLogout }}m {{ secondsToLogout }}s</0>'}
</Trans> where the key is automatically detected as |
Doesnt work man, error is still there |
The solution is:
|
While this solution resolves my current issue, it inadvertently permits objects as valid children types in other contexts. Are there alternative solutions that don't involve altering the JSX children type? |
@goorm-philip, as I understand it, the only two solutions possible (given i18next's design and React's type definitions) are to loosen React's type definitions (using |
For everyone still facing problems due to the breaking ReactNode type change, you could change the usage of the Trans component to something like so:
And the according translation with the References: https://react.i18next.com/latest/trans-component#overriding-react-component-props-v11.5.0 |
I have another idea for a workaround that I think is better than the others suggested so far
It should also be possible to make a linting rule to ensure that the helper is only used as a descendant of |
🐛 Bug Report
With version 18 of
@types/react
, Typescript complains when passing objects to theTrans
component.It previously worked because
ReactNode
had a| {}
in its definition, allowing arbitrary objects to be passed. That has been removed (for a good reason) in version 18 of the type definitions. See this PR for more information.To Reproduce
From this example:
This fails type checking:
TS2322: Type '{ name: void; }' is not assignable to type 'ReactNode'.
Expected behavior
Type checking works, i.e. the TS compiler does not complain.
I would think that
{}
(or something likeRecord<string, unknown>
) should be explicitly added to thechildren
type ofTrans
.Your Environment
The text was updated successfully, but these errors were encountered: