Migrate @fluent/react to TypeScript#458
Conversation
|
CC @huy-nguyen, @Seally, @Gregoor -- Would you be able to give me feedback on this PR? Thanks! |
React typings (wrongly) expect the return type of functional components to be a ReactElement, thus making () => "Hello" invalid. This is incorrect; as of React 16, it's possible to return plain strings from functional components and Component.render(). See DefinitelyTyped#18912 and TypeScript#21699. To work around this, annotate the return value of Localized to be of type ReactElement, and whenever a string or null are returned, wrap them in Fragments.
|
I had to work around the issue described in DefinitelyTyped/DefinitelyTyped#18912. React's typings currently expect the return type of |
|
I tested this in a few example projects. I think it's ready for review. I'd like to test it in a larger project, if possible, but I don't have good candidates yet. |
Gregoor
left a comment
There was a problem hiding this comment.
Two general things that are on my mind:
- migration path: This does require all consumers of @fluent/react to change a lot (most?) of their
Localizedcomponents, doesn't it? So this is a major version change? Is there room for allowing the old way and just throwing warnings for now, so that consumers can migrate gradually? - API / naming: I'm torn on this one. I like brevity but dislike abbreviations. You probably already made the right compromise here and it's not like I have a better one to offer. Just wanted to highlight it.
Exciting changes overall 🎉
| */ | ||
| function Localized(props) { | ||
| const { id, attrs, children: child = null } = props; | ||
| export function Localized(props: LocalizedProps): ReactElement { |
There was a problem hiding this comment.
Why not loosen the return type to ReactNode instead, which includes ReactElement but also allows child to be returned without a wrapping call.
There was a problem hiding this comment.
I did that initially, but I ran into DefinitelyTyped/DefinitelyTyped#18912. See #458 (comment).
| id: string; | ||
| attrs?: Record<string, boolean>; | ||
| children?: ReactNode; | ||
| vars?: Record<string, FluentArgument>; |
There was a problem hiding this comment.
Just out of curiosity: why vars instead of args?
There was a problem hiding this comment.
Hmm, I'm torn on this one. They're called variables in Fluent Syntax, but they're passed as arguments. In DOM, we use data-l10n-args, too, but that's mostly an artifact of how we did it in Firefox OS. I think vars is a more accurate name here, but args might be a better choice because of the prior work and due to how developers think about them.
There was a problem hiding this comment.
My main concern would be to be consistent, and I'd rather change the resolvers as we change resolvers anyway. As opposed to changing the syntax.
So, vars is my vote.
One could argue that you pass your variables as args. As in, when you pass them, they're args, when you store and reference them, they're variables, but that's not useful for anything, I think. (But that may be the historical reason why we have both.)
|
Thanks for the comments, @Gregoor!
Yes, it's a breaking change. We might want to release it as 1.0, to make that superclear. I'd also like to figure out the migration plan. We might be able to add the following index signature to
I went with |
| { | ||
| TEXT_NODE: 3, | ||
| nodeType: 3, | ||
| nodeName: "#text", |
There was a problem hiding this comment.
I simplified the detection of text nodes relying on the fact that their nodeName is guaranteed by the standard to be #text.
|
I've finished upgrading
I think the remaining work is about naming. |
I'd like to go ahead with |
@Gregoor I'd like to make this a breaking change in the next release of The alternative would be to support both APIs for a period of time, but I'm afraid of having to maintain and test both APIs at the same time, plus their combinations. |
Pike
left a comment
There was a problem hiding this comment.
I'm not feeling confident in actually reviewing this, sorry.
I've got some raised eyebrows on the changes to vendor. The files don't seem to match what's in the README anymore?
Good catch, I reverted them to their originals. |
See #376 for the general discussion about migrating fluent.js packages to TypeScript.
The migration of
@fluent/reactis more involved than the packages which I've migrated so far. I'll keep this PR in the draft state for the next couple of days because I'd like to test it in a TypeScript+React project.This PR changes the API of the
<Localized>component. To be fair, I should have made it this way from the get go. I regret giving in to the temptation of the "cute" API design in which arguments to the translations are passed in as props prefixed with the dollar sign:<Localized $arg={...}>. In this PR, the API changes to a more explicit<Localized vars={{arg: ...}}>.The same goes for elements which are then matched with markup from the translation. Rather than passing them as separate props:
<Localized confirmButton={...}>, this PR changes the API to be explicit again:<Localized elems={{confirmButton: ...}}>.I'm not sure if I got all the
ReactNodevs.ReactElementvs.ComponentTyperight. I'll need to test more and I'd appreciate help with testing!