-
Notifications
You must be signed in to change notification settings - Fork 2k
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
Migrate away from deprecated react types #78292
Conversation
Here is how your PR affects size of JS and CSS bundles shipped to the user's browser: App Entrypoints (~29 bytes added 📈 [gzipped])
Common code that is always downloaded and parsed every time the app is loaded, no matter which route is used. Sections (~136 bytes added 📈 [gzipped])
Sections contain code specific for a given set of routes. Is downloaded and parsed only when a particular route is navigated to. Async-loaded Components (~31 bytes added 📈 [gzipped])
React components that are loaded lazily, when a certain part of UI is displayed for the first time. Legend What is parsed and gzip size?Parsed Size: Uncompressed size of the JS and CSS files. This much code needs to be parsed and stored in memory. Generated by performance advisor bot at iscalypsofastyet.com. |
@@ -29,7 +29,7 @@ export default function AnnualHighlightCards( { | |||
|
|||
const header = ( | |||
<h3 className="highlight-cards-heading"> | |||
{ Number.isFinite( year ) | |||
{ year != null && Number.isFinite( year ) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
isFinite doesn't restrict the type enough :(
93a457d
to
1778353
Compare
In cd26e5e, I went through every instance of I also removed |
1936b0d
to
6a002b8
Compare
89bba1e
to
85338b5
Compare
In 85338b5, I went through all the
None of this should functionally change how these translations were working. Ideally, the types wouldn't be this loose in the first place. I really wish this part of our codebase was much better. Despite having strong type tools at our disposal, all of these issues happen because the vast majority of our code can't guarantee that some value exists. Part of this is the fault of the redux store -- (for example, when you get the site title, the site might not exist, and you might get no value back). But this means basically everything is doing way more null checks than we really should have to be doing. The other problem is when types aren't architected with guarantees in mind from the start. For example, a static map of information, with constant keys known ahead of time, and yet the function to get a value can return undefined. It should be using keyof, etc to get really solid type info around that. But this wasn't as big a problem as the first thing. |
Also, there is this eslint issue that might be a bug: TypeError: Cannot read properties of undefined (reading 'name')
Occurred while linting client/my-sites/marketing/do-it-for-me/difm-lite-in-progress.tsx:69
Rule: "wpcalypso/i18n-no-variables" I can't tell what it's seeing on that line, and neither does my IDE |
Fix coming up for this in #78305. I'll cherry-pick it here just to ensure that it fixes the issue, but we can land it separately if it does. |
addc29a
to
5425582
Compare
Seems like checks passed with #78305. I'll go ahead and merge it and rebase the branch again. |
5425582
to
d130754
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks for separating the deprecated React type changes, that really helps make the primary React upgrade PR smaller.
I've done a thorough review and made some suggestions, also pinged a few teams for follow-up fixes that could improve some of the strings in the case of nullish args.
There are a couple of things I think we can improve in this PR before shipping it - let me know what you think!
Fantastic work, @noahtallen! 🚀
@@ -277,7 +277,7 @@ export default function UpsellStep( { upsell, site, purchase, ...props }: StepPr | |||
'But we’d love to see you stick around to build on what you started. ' + | |||
'How about a free month of your %(currentPlan)s plan subscription to continue building your site?', | |||
{ | |||
args: { planName: getPlan( purchase.productSlug )?.getTitle() }, | |||
args: { planName: getPlan( purchase.productSlug )?.getTitle() ?? '' }, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The string looks almost good if planName
is an empty string. It will only have double spacing. cc @taggon
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm not convinced the plan ever doesn't exist here 🤔
siteTitle: siteTitle ?? '', | ||
siteAddress: siteDomain ?? '', |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The string would end up awkward with some additional spacing if siteTitle
and/or siteAddress
are empty. Is this something we can/should address as a side task, cc @Automattic/martech-decepticons?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think this is another scenario where the component probably doesn't make sense when the site doesn't exist, so I'd recommend improving types to assert that a site does indeed exist here
client/my-sites/checkout/composite-checkout/hooks/use-prepare-products-for-cart.ts
Outdated
Show resolved
Hide resolved
client/my-sites/checkout/composite-checkout/hooks/use-prepare-products-for-cart.ts
Outdated
Show resolved
Hide resolved
Thanks for the comments @sirbrillig and @tyxla! Ideally, we wouldn't have so many null issues in the first place, and have better guarantees through the type system. But that would take a lot of effort -- this PR has to fix nearly 300 different issues! Ultimately, my approach in the PR was to try to find some balance on each issue one by one. If it was easy to add a more thorough fix, I did so, but ultimately, this mostly doesn't change how these components work on trunk. In reality, a lot of this data exists at runtime, but many components/types aren't designed to have strong type guarantees. (Hence why I used For example, everything about a certain component might be designed with the assumption that a site is selected, and there could even be code that uses something like The vast majority of changes in this PR (related to I'll go through each of the comments so far and implement improvements, since those are likely higher impact, but I just wanted to mention most of this was an active consideration as I was working on the PR! There is not a straightforward solution, and at the end of the day, we'll need to improve our education around this so that teams know how to better utilize strong type guarantees. And hopefully the changes in this PR help people notice that more frequently! |
I opened #78350 to explore better types for plans, which showed up a lot in the code. I think it'd be relatively easy to get a lot stricter types for the related code since we know most of the info up front |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think this looks great! Thanks @noahtallen, great work as always. 💯
As you recognized, there are some compromises we're making, but it's for the greater good and we're heading towards stricter types in #78350 anyway. I think we should go ahead and ship this as-is.
I also think it would be a good idea if the pinged teams take a look at the code just in case. There could be some opportunities for improving the types or the logic to cover all those ede cases.
1162986
to
3842051
Compare
This Pull Request is now available for translation here: https://translate.wordpress.com/deliverables/8058917 Hi @noahtallen, could you please edit the description of this PR and add a screenshot for our translators? Ideally it'd include all of the following strings:
Thank you in advance! |
^ Unfortunately, I don't have a good way to test these flows, since I think they would only happen very rarely. It is low impact enough that I'm not very concerned about these strings |
Agreed 👍 And what we're adding is already an improvement to what we had before, so I'm not concerned at all either. |
Translation for this Pull Request has now been finished. |
Proposed Changes
React 18 includes fixes and changes for certain React types. Notably,
ReactFragment
is changing from{} | Iterable<ReactNode>
to simplyIterable<ReactNode>
. Additionally, theReactChild
andReactText
types have been deprecated.The first change is big because it makes
ReactFragment
much narrower -- so types which used to be accepted now aren't.The core place this impacts us is the translation function, which relied on
ReactFragment
before. With the{}
type, that means thetranslation
function currently accepts substitutions which might not exist. We don't want that behavior; we only want to accept substitutions which do exist.Since this requires a lot of changes, I propose we separate it from #77046 and work on it here:
@types/react
to simply remove{}
onReactFragment
. This lets us get the biggest part of the upgrade in the repo more quickly. (We'll remove this after React 18 #77046 is merged.)ReactChild
andReactText
across the repo to prevent new uses.ReactChild
andReactText
Testing Instructions
Typecheck should pass. Most changes should be to types.