Follow-up to #8386 (Listing block: add an offset to skip the first N results).
Problem
The Listing block's querystring path is still JavaScript, while the code it now depends on is not. #8386 added helpers/Pagination/Pagination.ts and ported actions/querystringsearch/querystringsearch.ts, so the arithmetic and the request payload are typed. The three modules around them are not:
components/manage/Blocks/Listing/withQuerystringResults.jsx
components/manage/Blocks/Listing/getAsyncData.js
components/manage/Widgets/QuerystringWidget.jsx
Converting them is straightforward, but it is not free, and the cost belongs in its own review rather than riding along with a feature. This issue records what the work involves and what it changes.
What needs to change
1. Convert the three modules to .tsx / .ts. QuerystringData and IntegerLike already exist and cover most of what they need. QuerystringWidget's schema is a JSONSchema from @plone/types in every respect but its missing title, so Omit<JSONSchema, 'title'> types it exactly. The reducer slice withQuerystringResults reads has no store-wide type to draw on, so it needs a local SelectorState, following the pattern already used in Controlpanels/BlockType.tsx.
2. Fix two JSDoc annotations that are wrong. TypeScript reads JSDoc from .js files, and these two currently describe signatures their callers do not use:
helpers/Utils/usePagination.js documents no parameters at all. Its id = null default makes TypeScript infer a null-only parameter, so passing the block id — a string — is an error.
actions/content/content.js declares @param {string} version and @param {string} subrequest, but both default to null and callers pass null. page is not documented at all, so it infers as null and passing a page number is an error.
Both fixes are widenings (string|null, and documenting page), so they cannot break an existing caller.
3. Add @types/hoist-non-react-statics as a devDependency. hoist-non-react-statics is already a Volto dependency; it just ships no types, so withQuerystringResults cannot import it from TypeScript.
The part worth discussing
@types/hoist-non-react-statics is not a local change. hoistNonReactStatics is what connect() uses, so once its types resolve, the emitted declarations change everywhere it appears. Running pnpm build:types with it installed rewrites 49 files under packages/volto/types/, e.g.:
-declare const _default: any;
+declare const _default: (React.ForwardRefExoticComponent<React.RefAttributes<any>> & import("hoist-non-react-statics").NonReactStatics<any, {}>) | ...
This is better typing — those exports are currently any. But packages/volto/types/ is committed and regenerated at release time, so the change would land in a release rather than in the PR that caused it, and add-ons that type-check against those previously-any exports could start seeing errors. That is a deliberate decision for this issue to make, not a side effect to absorb.
Notes
tsconfig.declarations.json includes src/**/*.js, *.jsx and *.tsx, but not *.ts. Declarations for the new .ts modules are still emitted because they are pulled in transitively, but the omission is worth a look.
- The strict
tsconfig.json has a pre-existing baseline of 12 errors, all in .stories.tsx files and Widgets/index.tsx. The conversions add none; the baseline is unrelated and could be cleaned up separately.
- Two casts are unavoidable until the modules underneath carry types:
dispatch (Volto's API middleware takes request actions, not AnyAction) and ObjectWidget (untyped, so TypeScript infers every destructured prop as required).
Acceptance criteria
Prior art
All of this was written and verified while working on #8386, then deliberately left out to keep that PR backportable and reviewable. The conversions passed the full suite with no behaviour change and added no errors under the strict tsconfig.json. They were reverted rather than merged, so whoever picks this up starts from scratch — but the findings above are the map, and the decision in "The part worth discussing" is the reason this is not just a mechanical rename.
Follow-up to #8386 (Listing block: add an offset to skip the first N results).
Problem
The Listing block's querystring path is still JavaScript, while the code it now depends on is not. #8386 added
helpers/Pagination/Pagination.tsand portedactions/querystringsearch/querystringsearch.ts, so the arithmetic and the request payload are typed. The three modules around them are not:components/manage/Blocks/Listing/withQuerystringResults.jsxcomponents/manage/Blocks/Listing/getAsyncData.jscomponents/manage/Widgets/QuerystringWidget.jsxConverting them is straightforward, but it is not free, and the cost belongs in its own review rather than riding along with a feature. This issue records what the work involves and what it changes.
What needs to change
1. Convert the three modules to
.tsx/.ts.QuerystringDataandIntegerLikealready exist and cover most of what they need.QuerystringWidget's schema is aJSONSchemafrom@plone/typesin every respect but its missingtitle, soOmit<JSONSchema, 'title'>types it exactly. The reducer slicewithQuerystringResultsreads has no store-wide type to draw on, so it needs a localSelectorState, following the pattern already used inControlpanels/BlockType.tsx.2. Fix two JSDoc annotations that are wrong. TypeScript reads JSDoc from
.jsfiles, and these two currently describe signatures their callers do not use:helpers/Utils/usePagination.jsdocuments no parameters at all. Itsid = nulldefault makes TypeScript infer anull-only parameter, so passing the block id — a string — is an error.actions/content/content.jsdeclares@param {string} versionand@param {string} subrequest, but both default tonulland callers passnull.pageis not documented at all, so it infers asnulland passing a page number is an error.Both fixes are widenings (
string|null, and documentingpage), so they cannot break an existing caller.3. Add
@types/hoist-non-react-staticsas a devDependency.hoist-non-react-staticsis already a Volto dependency; it just ships no types, sowithQuerystringResultscannot import it from TypeScript.The part worth discussing
@types/hoist-non-react-staticsis not a local change.hoistNonReactStaticsis whatconnect()uses, so once its types resolve, the emitted declarations change everywhere it appears. Runningpnpm build:typeswith it installed rewrites 49 files underpackages/volto/types/, e.g.:This is better typing — those exports are currently
any. Butpackages/volto/types/is committed and regenerated at release time, so the change would land in a release rather than in the PR that caused it, and add-ons that type-check against those previously-anyexports could start seeing errors. That is a deliberate decision for this issue to make, not a side effect to absorb.Notes
tsconfig.declarations.jsonincludessrc/**/*.js,*.jsxand*.tsx, but not*.ts. Declarations for the new.tsmodules are still emitted because they are pulled in transitively, but the omission is worth a look.tsconfig.jsonhas a pre-existing baseline of 12 errors, all in.stories.tsxfiles andWidgets/index.tsx. The conversions add none; the baseline is unrelated and could be cleaned up separately.dispatch(Volto's API middleware takes request actions, notAnyAction) andObjectWidget(untyped, so TypeScript infers every destructured prop as required).Acceptance criteria
tsc --project tsconfig.jsonusePaginationandgetContentJSDoc describe the signatures their callers actually use@types/hoist-non-react-staticsis agreed on, or the dependency is avoidedPrior art
All of this was written and verified while working on #8386, then deliberately left out to keep that PR backportable and reviewable. The conversions passed the full suite with no behaviour change and added no errors under the strict
tsconfig.json. They were reverted rather than merged, so whoever picks this up starts from scratch — but the findings above are the map, and the decision in "The part worth discussing" is the reason this is not just a mechanical rename.