Conversation
There was a problem hiding this comment.
Copilot review overview
🔵 Needs a closer look
The plugin-facing hook bridge does not yet enforce the known hook signatures.
Review effort: Lite
Findings: None
What changed in this PR
This PR strengthens frontend typings and reduces shared any/Function usage as groundwork for eliminating application typing violations.
Changes:
- Types tablesorter integrations and dynamic component outputs.
- Adds typed
HookServicesignatures and validation tests. - Removes obsolete declarations and exempts test code from selected lint rules.
| File | Summary |
|---|---|
frontend/src/typings/shims.d.ts |
Types jQuery tablesorter APIs. |
frontend/src/typings/open-project.typings.d.ts |
Removes unused global declarations. |
frontend/src/stimulus/controllers/dynamic/reporting/page.controller.ts |
Uses updated tablesorter typings. |
frontend/src/app/shared/components/fields/edit/field-types/select-edit-field/select-edit-field.component.ts |
Types dynamic output handlers. |
frontend/src/app/features/work-packages/routing/partitioned-query-space-page/partitioned-query-space-page.component.ts |
Types dynamic component outputs. |
frontend/src/app/features/work-packages/components/wp-table/context-menu-helper/wp-context-menu-helper.service.ts |
Types bulk actions. |
frontend/src/app/features/work-packages/components/wp-list/wp-list-checksum.service.ts |
Replaces a Function callback type. |
frontend/src/app/features/plugins/hook-service.ts |
Adds typed hook signatures and custom-hook fallback. |
frontend/src/app/features/plugins/hook-service.spec.ts |
Tests known-hook signature enforcement. |
frontend/eslint.config.mjs |
Exempts tests and helpers from selected unsafe typing rules. |
💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.
b3c3e4d to
61400e7
Compare
|
Warning Flaky specs
🤖 Ask Copilot to investigateCopy the prompt below into a new comment on this PR to delegate the investigation to GitHub Copilot. It will look into the flakiness and open a separate pull request with you as reviewer. |
Turns off no-explicit-any and the no-unsafe-* rules for specs and the test helpers that are kept out of the production bundle. Test doubles and expectation helpers routinely work with loosely typed values, and the typing effort under OP-20341 targets application code only. The two no-unsafe exemptions that lived in the vitest spec block move into the new block, which covers the helper files as well. Removes the inline disables in specs and test setup that the exemption makes redundant. https://community.openproject.org/wp/OP-20342
Removes the api.v3 Result, Collection and Duration interfaces, the Factory global and the op.QueryParams namespace. Nothing references them, and most declared their members as any. https://community.openproject.org/wp/OP-20342
Declares the tablesorter call and its language defaults, and the metadata plugin slot the reporting page clears, instead of any. The reporting controller no longer needs its inline disables for them. https://community.openproject.org/wp/OP-20342
Adds a HookSignatures map for the hooks the core registers and calls, so register and call check callback parameters and return typed results instead of any. Arbitrary hook names still fall back to an open signature, because plugins register and call hooks by name. The fallback excludes the known names, so a mismatched callback or argument for a known hook fails to compile; the spec pins that down. The bulk context menu actions are now declared as WorkPackageAction, which the typed hook result exposed as narrower than its use. https://community.openproject.org/wp/OP-20342
Types the dynamic component output maps with ng-dynamic-component's EventHandler, which is what ndcDynamicOutputs accepts, and the checksum callback as a plain thunk. The unconstrained Function type accepted any callable and let calls through unchecked. https://community.openproject.org/wp/OP-20342
61400e7 to
661dab8
Compare
|
Warning Flaky specs
🤖 Ask Copilot to investigateCopy the prompt below into a new comment on this PR to delegate the investigation to GitHub Copilot. It will look into the flakiness and open a separate pull request with you as reviewer. |
lwassermann
left a comment
There was a problem hiding this comment.
Great initiative 👍 💯
This is a bit sprinkled about, but since the changes are all for static checks, I'm not worried.
| workPackageNewInitialization:(change:WorkPackageChangeset) => void; | ||
| workPackageSingleContextMenu:() => WorkPackageAction; | ||
| workPackageTableContextMenu:() => WorkPackageAction; | ||
| } |
There was a problem hiding this comment.
Updating this list would now be up to the registrees of the Hook service, wouldn't it?
In my understanding, this pattern where modules register themselves is an attempt to decouple the specific implementations from being depended on by the central registry. In the past, I had the impression that it hides more than it shows. Since we already have the specific types here, maybe we can also remove the registering and just register them all explicitly when this module loads.
Or, if that is not a feasible refactoring, instead of having all the signatures here, every module that registers itself would also extend the HookSignatures interface? Or does this run into other problems? I've read that the core difference between type A = {} and interface A {} is that the interface is open and can be extended, but I'm not sure I've seen that in action. So this might not be possible 😅.
There was a problem hiding this comment.
Updating this list would now be up to the registrees of the Hook service, wouldn't it?
Not quite. The contract lives with the caller.
Adding a new hook.call(...) will mean adding its signature here, Plugins registering callbacks will never touch this list. Unknown hook ids will still fall through to the untyped overload, so nothing breaks.
Declaration merging would work (declare module 'core-app/features/plugins/hook-service' { interface HookSignatures { ... } } next to each call site) and would drop the imports here. But it scatters the hooks back across the codebase, and I think the central list is what makes the indirection visible, which is your concern.
Replacing self-registration with explicit wiring is worth doing, but it's out of scope for a typing PR IMO: two plugins register through pluginContext.hooks (untyped, outside this interface), and four hooks (gridWidgets, workPackageAttachmentUploadComponent, workPackageBulkContextMenu, workPackageNewInitialization) have no in-repo registrant at all. Removing the dead ones would be part of that decision. I'd prefer to tackle it in a follow-up PR, if that works for you?
There was a problem hiding this comment.
I've created OP-20381 to follow up on this. Feedback appreciated! 🙏🏻
Ticket
https://community.openproject.org/wp/OP-20342
What are you trying to accomplish?
Lays the groundwork for OP-20341 (zero ESLint typing violations in frontend application code) by removing shared sources of
any.no-explicit-anyand theno-unsafe-*rules; the typing effort targets application code.api.v3Result/Collection/Duration,Factory,op.QueryParams) and types the tablesorter jQuery plugin.HookServicecallbacks by hook name.Functiontypes with real signatures.Application-code typing violations drop from 1,203 to 1,174.
What approach did you choose and why?
HookServicegets aHookSignaturesmap for the ten hooks the core registers and calls. Plugins register and call hooks by arbitrary name, so both methods keep an open fallback overload; the fallback excludes the known names, so a mismatched callback or argument for a known hook fails to compile. The spec pins this down with@ts-expect-errorcases. Typing the hooks exposed that the bulk context menu actions were inferred narrower thanWorkPackageAction.The dynamic component output maps use
ng-dynamic-component'sEventHandler, which is whatndcDynamicOutputsaccepts.Stacked on #25656 because both touch the same declaration files; GitHub retargets this to
devonce #25656 merges. TheFunction.$linkglobal and thedom-plane/onboarding declarations stay for the HAL resources and third-party tickets, which type their consumers.AI involvement
Directed – I specified the requirements and AI implemented most of it; I validated via testing rather than a full line-by-line review.
Merge checklist
Browser JavaScript diff (1 files, type-only changes omitted)
Each changed browser file (TypeScript and templates, excluding specs, test helpers and declaration files) is transpiled on its own with
frontend/tsconfig.json, before and after; this is the emitted JavaScript that differs.