Convert data package fully to TS#76149
Conversation
|
Size Change: +10 B (0%) Total Size: 6.87 MB
ℹ️ View Unchanged
|
e298423 to
dede8b3
Compare
|
The following accounts have interacted with this PR and/or linked issues. I will continue to update these lists as activity occurs. You can also manually ask me to refresh this list by adding the If you're merging code through a pull request on GitHub, copy and paste the following into the bottom of the merge commit message. To understand the WordPress project's expectations around crediting contributors, please review the Contributor Attribution page in the Core Handbook. |
ObliviousHarmony
left a comment
There was a problem hiding this comment.
Thanks @manzoorwanijk,
I'm really glad you're taking this on, I'm excited to see these improvements land. Aside from my review comments, I have a few other thoughts:
Types Using Store Name
I know that, ideally, everyone would be using descriptors, but the world is imperfect. I propose adding an empty interface StoreRegistry {} type that downstream consumers augment like this:
declare module '@wordpress/data' {
interface StoreRegistry {
'core/block-editor': BlockEditorStoreDescriptor;
}
}We would then provide another overload for select, resolveSelect, suspendSelect, and dispatch that takes in StoreRegistry[ StoreNameOrDescriptor ] and returns the typed content if one is registered.
createRegistryControl Type
While this PR doesn't change this function, I noticed that the type is incorrect. It uses (...args: any) => any, however, shouldn't that be (registry: DataRegistry) => (...args: any) => any?
PromisifyActionCreator Generator Return Types
For generator-based action creators, this currently returns Promise<Generator<...>> instead of the generator's return type. I think it needs a conditional check before the fallthrough so that it correctly resolves Promise<T> to the function's return type.
We moved away from using store names. See #27088 |
|
@ObliviousHarmony thank you for the review. I have addressed your feedback. |
|
Flaky tests detected in 1885d1c. 🔍 Workflow run URL: https://github.com/WordPress/gutenberg/actions/runs/22722353296
|
b34a4c4 to
1885d1c
Compare
|
Size Change: +10 B (0%) Total Size: 6.89 MB
ℹ️ View Unchanged
|
Currently, CurriedSelectorsOf includes the Meta selectors but ActionCreatorsOf doesn't, which we have to duplicate everywhere. So it's better to make them include the meta actions/selectors by default.
ObliviousHarmony
left a comment
There was a problem hiding this comment.
I'm going to go ahead and approve, these types are a significant improvement, thanks @manzoorwanijk. In addition to my review of the types, I also created a test harness locally and confirmed all of the expected behavior.
We moved away from using store names. See #27088
I know, but as an external consumer of these types, a simple augmentable map like the one I proposed is a low-effort addition that provides type-safety to legacy code and makes migrations smoother.
One clear benefit I would advocate for is that it makes migrating from DT packages to native types simpler. @types packages work using select and dispatch overloads that add the store name. Once native types are shipped, maintaining type safety means replacing every instance of the store name with the store descriptor. A StoreRegistry store name -> descriptor map covers type safety for both cases.
A new interface:
export interface StoreRegistry {}
With another select/resolveSelect/suspendSelect/dispatch overload:
<K extends keyof StoreRegistry>(store: K): CurriedSelectorsOf<StoreRegistry[K]>;
Then projects provide a mapping:
declare module '@wordpress/data' {
interface StoreRegistry {
'core': typeof import('@wordpress/core-data').store;
}
}
After that, the descriptor is passed around and the rest of the machinery works as expected. We let TypeScript's module augmentation do the heavy lifting!
I think we can explore that in a separate PR, and it will then need to be done for all the packages that have stores. |
What?
See #76148, also a part of #67691
Converts all remaining JavaScript source files in
@wordpress/datato TypeScript.Why?
The
@wordpress/datapackage is a foundational piece of the Gutenberg data layer, but most of its source files were still plain JavaScript with JSDoc annotations. Converting to TypeScript improves type safety, catches bugs at compile time, and provides better IDE support for both contributors and consumers. This also unblocks downstream packages from having to use@ts-expect-errorfor metadata selectors/actions likeisResolvingandinvalidateResolutionthat are injected at runtime but weren't reflected in the types.This clears two blockers for the
tsgomigration (#76148):#2— JSDoc@typedefwith@templategenerates broken declarations intsgo. The@wordpress/datapackage was one of the primary users of this pattern. Converting to native TypeScript eliminates the issue entirely.#3—packages/datanot being fully/properly typed. Most downstream type errors originated from this package's incomplete JSDoc types. Full TypeScript conversion resolves the majority of those cascading errors.How?
.js→.ts/.tsxusinggit mvto preserve file history. Files with JSX were renamed to.tsx.types.tswith new interfaces:DataRegistry,InternalStoreInstance,NormalizedResolver,BoundSelector,MetadataSelectors,MetadataActions, and more.CurriedSelectorsOfnow intersects withMetadataSelectorsandUseDispatchReturn/DispatchReturnintersect withMetadataActions, so resolution-tracking APIs (isResolving,hasFinishedResolution,invalidateResolution,finishResolution, etc.) are properly typed on every store. These types are derived directly from the actual metadata module exports — no manual duplication.DataRegistry.selectandDataRegistry.dispatchto support bothStoreDescriptor(typed) andstring(dynamic) arguments.@ts-expect-errordirectives in downstream packages (global-styles-ui,media-utils) that were suppressing errors for metadata selectors/actions now properly typed.Files converted (28 files)
registry.js→registry.tscontrols.js→controls.tsdefault-registry.js→default-registry.tspromise-middleware.js→promise-middleware.tsresolvers-cache-middleware.js→resolvers-cache-middleware.tsredux-store/index.js→redux-store/index.tsredux-store/metadata/selectors.js→redux-store/metadata/selectors.tsstore/index.js→store/index.tsplugins/index.js→plugins/index.tsplugins/persistence/index.js→plugins/persistence/index.tsplugins/persistence/storage/default.js→default.tsplugins/persistence/storage/object.js→object.tsuse-select,use-dispatch,with-select,with-dispatch,with-registry,async-mode-provider,registry-provider)Testing Instructions
npm run build— verify the build succeeds.With some assistance from Claude Code