Skip to content
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

Update to typescript 5.1 #74540

Merged
merged 12 commits into from
Aug 1, 2023
Merged

Update to typescript 5.1 #74540

merged 12 commits into from
Aug 1, 2023

Conversation

noahtallen
Copy link
Contributor

@noahtallen noahtallen commented Mar 16, 2023

Proposed Changes

This PR starts the update to Typescript 5, which was just released. I mostly wanted to see how much extra work this would require (if any). So far, a couple issues have come up, which we can track here.

See also: WordPress/gutenberg#52621

Testing Instructions

  • CI should pass (no type errors)
  • Smoke test all of calypso

To Do:

  • Fix an issue with generic inference related to treeSelect and createSelector. This was hard to track down, but it might be a TS bug: JS generic inference change after upgrading to 5.1 microsoft/TypeScript#55192. This was fixed by duplicating the generics which had multiple candidates, extending from the generic we want TS to pick.
  • cloneElement and isValidElement have some new issues where types won't narrow in the same way as before if no generics are passed to props.
  • The sharp dependency is causing issues. This can be removed here. (Remove unused gutenboarding script #74539)
  • Some equality checks in calypso-e2e/src/rest-api-client.ts are incorrect (E2E Framework: fix empty array check in RestAPIClient. #74741)
  • PopperJS build types have issues. Fixed by updating to a newer minor version of Popper. (Side note, this package was renamed to something else.)
  • There is type issue in the ButtonBar component. Typescript doesn't think that ReactElement includes the className property, along with several other properties. This was caused by an issue with isValidElement not narrowing types correctly: 5.0: Narrowing from type-guards ignored when constructing JSX call (2604) microsoft/TypeScript#53178 (comment)
  • There are a handful of useContext calls which rely on a GlobalStylesContext import from @wordpress/edit-site. It now thinks the type is unknown, but previously it didn't complain about any issues here. (Example issue) These global styles issues were fixed by that team.
  • @wordpress/data's build types cause errors in ETK. Appears to be resolved when updating to newer wordpress data versions. (Potentially blocking the PR on React 18)
  • @wordpress/data-control's types cause errors in ETK. This might require a DT update. (See here)

@noahtallen noahtallen requested a review from a team March 16, 2023 20:36
@noahtallen noahtallen self-assigned this Mar 16, 2023
@matticbot matticbot added the [Status] Needs Review The PR is ready for review. This also triggers e2e canary tests and wp-desktop tests automatically. label Mar 16, 2023
@github-actions
Copy link

github-actions bot commented Mar 16, 2023

Copy link
Member

@tyxla tyxla left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for starting work on this 👍

While I haven't thoroughly read through the changelog, I'm assuming that the new version could have a bunch of fixes which make tests more precise.

One example I noticed is the group of errors related to unsupported props. I think that error actually makes sense and here's an example how I made it work locally:

diff --git a/packages/components/src/dialog/button-bar.tsx b/packages/components/src/dialog/button-bar.tsx
index 22b852296c..777cf2f166 100644
--- a/packages/components/src/dialog/button-bar.tsx
+++ b/packages/components/src/dialog/button-bar.tsx
@@ -1,7 +1,7 @@
 import classnames from 'classnames';
 import { isValidElement, cloneElement } from 'react';
 import Button from '../button';
-import type { ReactElement, ReactNode, FunctionComponent } from 'react';
+import type { ReactNode, FunctionComponent } from 'react';

 export type BaseButton = {
        action: string;
@@ -15,10 +15,8 @@ export type BaseButton = {
        target?: string;
 };

-export type Button = ReactElement | BaseButton;
-
 type Props = {
-       buttons?: Button[];
+       buttons?: BaseButton[];
        baseClassName: string;
        onButtonClick: ( button: BaseButton ) => void;
 };
diff --git a/packages/components/src/dialog/index.tsx b/packages/components/src/dialog/index.tsx
index 819dc4a221..a62ae3f88b 100644
--- a/packages/components/src/dialog/index.tsx
+++ b/packages/components/src/dialog/index.tsx
@@ -3,7 +3,7 @@ import { useCallback } from 'react';
 import Modal from 'react-modal';
 import Gridicon from '../gridicon';
 import ButtonBar from './button-bar';
-import type { Button, BaseButton } from './button-bar';
+import type { BaseButton } from './button-bar';
 import type { PropsWithChildren } from 'react';

 import './style.scss';
@@ -12,7 +12,7 @@ type Props = {
        additionalClassNames?: Parameters< typeof classnames >[ 0 ];
        additionalOverlayClassNames?: Parameters< typeof classnames >[ 0 ];
        baseClassName?: string;
-       buttons?: Button[];
+       buttons?: BaseButton[];
        className?: string;
        isBackdropVisible?: boolean;
        isFullScreen?: boolean;

That being said, I wonder if there was a good reason it was typed that way before 🤔

Those new "Cannot find module '@automattic/components' or its corresponding type declarations." errors are also interesting. Any idea what changed with regards to module resolution?

@noahtallen
Copy link
Contributor Author

noahtallen commented Mar 21, 2023

Those new "Cannot find module '@automattic/components' or its corresponding type declarations." errors are also interesting. Any idea what changed with regards to module resolution?

I think this happens when there's a type issue compiling an internal dependency. Since other packages depend on it, they now fail since the dependency on components fails.

Thanks for looking into that; I'll try to integrate it!

@noahtallen
Copy link
Contributor Author

noahtallen commented Mar 21, 2023

I like your fix for the buttons! I think the reason we allowed the Button type is because this logic exists:

if ( isValidElement( button ) ) {
return cloneElement( button, { key } );
}

But it looks like just using BaseButton still allows normal buttons to be passed in

@@ -40,7 +40,7 @@ export const ListTile = ( {
typeof trailing === 'string' ? (
<div className="list-tile__trailing">{ trailing }</div>
) : isValidElement( trailing ) ? (
cloneElement( trailing, {
cloneElement( trailing as React.ReactElement, {
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

From what I can tell, this is still correct -- leading can be either ReactNode or ReactElement. The isValidElement check means we know it's an element here, which means we can access its props directly below.

Though, this seems to indicate that the type restriction from isValidElement isn't working out of the box any more 🤔

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The trouble here is that TS doesn't know if className is a valid prop for the trailing element. isValidElement assures that it's of type ReactElement<unknown>, while asserting it as ReactElement makes it ReactElement<any>.

I'm confused by the six overloads of cloneElements type, but apparently the difference between unknown and any matters.

I was able to fix the type error by calling:

isValidElement< { className: string } >( trailing )

Ideally we should specify a more specific type on leading and trailing, saying that it must be something on what we can put a className prop.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Added the < { className: string } > fix

Copy link
Contributor Author

@noahtallen noahtallen Aug 1, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Added this in one other place, but I noticed another issue where isValidElement won't narrow to ReactElement whatsoever, even when we don't care about the type of the props. See microsoft/TypeScript#53178 (comment)

In that case, it was easy to fix with our own wrapper function:

function isElement( el ): el is ReactElement {
  return isValidElement( el );
}

This was the root cause of issues with some base button types where you could pass either a react element or an object

@@ -3,7 +3,7 @@ import wpcomRequest from 'wpcom-proxy-request';
import type { GlobalStylesObject } from '../types';

const useGetGlobalStylesBaseConfig = ( siteId: number | string, stylesheet: string ) => {
return useQuery< any, unknown, unknown >(
return useQuery(
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This was preventing type inference from working -- the type parameter to wpcomRequest below was actually enough to get the return type inferred correctly. (But that didn't work with any/unknown type params to useQuery)

@matticbot
Copy link
Contributor

matticbot commented Mar 22, 2023

Here is how your PR affects size of JS and CSS bundles shipped to the user's browser:

App Entrypoints (~386 bytes added 📈 [gzipped])

name                   parsed_size           gzip_size
entry-stepper              +1444 B  (+0.1%)     +342 B  (+0.1%)
entry-main                 +1409 B  (+0.1%)     +324 B  (+0.1%)
entry-subscriptions        +1278 B  (+0.1%)     +280 B  (+0.1%)
entry-login                +1278 B  (+0.1%)     +280 B  (+0.1%)
entry-domains-landing       +950 B  (+0.2%)     +207 B  (+0.1%)

Common code that is always downloaded and parsed every time the app is loaded, no matter which route is used.

Sections (~352 bytes added 📈 [gzipped])

name                             parsed_size           gzip_size
pattern-assembler-step               +1081 B  (+0.1%)     +433 B  (+0.1%)
scan                                 +1018 B  (+0.1%)     +420 B  (+0.2%)
media                                +1018 B  (+0.1%)     +418 B  (+0.1%)
backup                               +1018 B  (+0.1%)     +420 B  (+0.2%)
marketplace                          +1006 B  (+0.1%)     +415 B  (+0.2%)
import-flow                           +962 B  (+0.0%)     +408 B  (+0.0%)
themes                                +899 B  (+0.1%)     +392 B  (+0.2%)
theme                                 +899 B  (+0.1%)     +392 B  (+0.2%)
hosting                               +899 B  (+0.1%)     +395 B  (+0.2%)
settings-performance                  +889 B  (+0.2%)     +388 B  (+0.3%)
jetpack-cloud-plugin-management       +751 B  (+0.1%)     +374 B  (+0.1%)
plugins                               +691 B  (+0.0%)     +370 B  (+0.1%)
plans                                 -445 B  (-0.0%)      -77 B  (-0.0%)
domains                               -226 B  (-0.0%)      -29 B  (-0.0%)
add-ons                               -168 B  (-0.1%)      -15 B  (-0.0%)
checkout                              -144 B  (-0.0%)      -43 B  (-0.0%)
home                                  -119 B  (-0.0%)      -25 B  (-0.0%)
woocommerce-installation              -116 B  (-0.0%)       -9 B  (-0.0%)
subscribers                           -116 B  (-0.0%)       -9 B  (-0.0%)
site-setup-flow                       -109 B  (-0.0%)      -22 B  (-0.0%)
migrate                               -109 B  (-0.0%)      -21 B  (-0.0%)
import-hosted-site-flow               -109 B  (-0.0%)      -20 B  (-0.0%)
earn                                  -109 B  (-0.0%)      -20 B  (-0.0%)
stats                                 -106 B  (-0.0%)       -3 B  (-0.0%)
site-purchases                         +75 B  (+0.0%)      +11 B  (+0.0%)
purchases                              +75 B  (+0.0%)      +13 B  (+0.0%)
update-design-flow                     -50 B  (-0.0%)      -16 B  (-0.0%)
link-in-bio-tld-flow                   -50 B  (-0.0%)      -10 B  (-0.0%)

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 (~383 bytes added 📈 [gzipped])

name                                                                         parsed_size           gzip_size
async-load-automattic-design-preview                                             +1071 B  (+0.1%)     +427 B  (+0.1%)
async-load-design-blocks                                                         +1018 B  (+0.1%)     +417 B  (+0.1%)
async-load-calypso-post-editor-editor-media-modal                                +1018 B  (+0.1%)     +418 B  (+0.1%)
async-load-design-wordpress-components-gallery                                   +1008 B  (+0.2%)     +413 B  (+0.3%)
async-load-calypso-post-editor-media-modal                                       +1008 B  (+0.1%)     +413 B  (+0.1%)
async-load-signup-steps-plans-theme-preselected                                   -169 B  (-0.1%)      -33 B  (-0.1%)
async-load-signup-steps-plans                                                     -169 B  (-0.1%)      -34 B  (-0.1%)
async-load-calypso-my-sites-plan-features-2023-grid                               -160 B  (-0.1%)      -16 B  (-0.0%)
async-load-calypso-my-sites-checkout-modal                                        -132 B  (-0.0%)      -35 B  (-0.0%)
async-load-calypso-blocks-editor-checkout-modal                                   -132 B  (-0.0%)      -33 B  (-0.0%)
async-load-signup-steps-domains                                                   -119 B  (-0.0%)      -25 B  (-0.0%)
async-load-design                                                                 -106 B  (-0.0%)       -4 B  (-0.0%)
async-load-signup-steps-page-picker                                                -78 B  (-0.2%)      -15 B  (-0.1%)
async-load-automattic-global-styles-src-components-global-styles-variations        +39 B  (+0.0%)      +16 B  (+0.0%)
async-load-signup-steps-plans-atomic-store                                         -19 B  (-0.0%)       -5 B  (-0.0%)

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.
Gzip Size: Compressed size of the JS and CSS files. This much data needs to be downloaded over network.

Generated by performance advisor bot at iscalypsofastyet.com.

@noahtallen noahtallen force-pushed the update-to-typescript-5 branch 2 times, most recently from 798a8a8 to 49134c1 Compare March 22, 2023 19:49
@matticbot
Copy link
Contributor

This PR modifies the release build for editing-toolkit

To test your changes on WordPress.com, run install-plugin.sh editing-toolkit update-to-typescript-5 on your sandbox.

To deploy your changes after merging, see the documentation: PCYsg-mMA-p2

@noahtallen
Copy link
Contributor Author

Pretty much everything is fixed now, but there are some issues with internal WordPress data and data control types when compiling ETK. I don't understand why those aren't problematic for the other packages, but they do seem to be resolved when I temporarily update wp data to a newer version. (So this might be blocked on React 18.)

Unless this is an internal problem, I think we may need to update the DT definitions for WP data controls as well.

@tyxla
Copy link
Member

tyxla commented Mar 23, 2023

Pretty much everything is fixed now, but there are some issues with internal WordPress data and data control types when compiling ETK. I don't understand why those aren't problematic for the other packages, but they do seem to be resolved when I temporarily update wp data to a newer version. (So this might be blocked on React 18.)

Unless this is an internal problem, I think we may need to update the DT definitions for WP data controls as well.

I'd also expect that most of the data controls usage could be refactored to simpler means. The generators are no longer in use in Gutenberg and we should likely follow suite.

@noahtallen
Copy link
Contributor Author

Rebased and updated to Typescript 5.1. Will then get this finished after #78711!

beginning!: number;
end?: number;
data: ReportData = new Map();
startCollectors!: Collector[];
stopCollectors!: Collector[];

// Stubs that are necessary to implement but are not used.
body = null;
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Typescript's definitions now require us to implement these, but we don't seem to use them anywhere.

@noahtallen noahtallen changed the title Update to typescript 5 Update to typescript 5.1 Jul 14, 2023
@noahtallen noahtallen force-pushed the update-to-typescript-5 branch 3 times, most recently from 5d2f349 to a07b296 Compare July 21, 2023 21:30
@noahtallen noahtallen requested a review from tyxla August 1, 2023 01:24
@noahtallen noahtallen marked this pull request as ready for review August 1, 2023 01:24
@noahtallen noahtallen requested review from a team and worldomonation as code owners August 1, 2023 01:24
@noahtallen noahtallen requested review from jessie-ross and removed request for a team August 1, 2023 01:24
@worldomonation
Copy link
Contributor

I've triggered Pre-Release Tests against the calypso.live image and it passed.

@jessie-ross jessie-ross removed their request for review August 1, 2023 06:33
Copy link
Member

@tyxla tyxla left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Great work @noahtallen 🚀

@noahtallen noahtallen merged commit df50fc7 into trunk Aug 1, 2023
4 checks passed
@noahtallen noahtallen deleted the update-to-typescript-5 branch August 1, 2023 22:20
@github-actions github-actions bot removed the [Status] Needs Review The PR is ready for review. This also triggers e2e canary tests and wp-desktop tests automatically. label Aug 1, 2023
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

7 participants