Skip to content

[OP-20349] Frontend typings: HalResource index signature - #25661

Open
myabc wants to merge 5 commits into
implementation/op-20344-eslint-typing-fix-hal-resourcesfrom
implementation/op-20349-eslint-typing-fix-hal-index-signature
Open

myabc wants to merge 5 commits into
implementation/op-20344-eslint-typing-fix-hal-resourcesfrom
implementation/op-20349-eslint-typing-fix-hal-index-signature

Conversation

@myabc

@myabc myabc commented Sep 28, 2026 •

Copy link
Copy Markdown
Contributor

Ticket

https://community.openproject.org/wp/OP-20349

What are you trying to accomplish?

Part of OP-20341: replaces the any index signature on HalResource with unknown. The any signature let every undeclared attribute read skip type checking; a code comment had tracked it as a source of bugs since 2019 (#31462).

Application-code typing violations drop from 664 to 531, mostly no-unsafe-* hits downstream of attribute reads.

What approach did you choose and why?

Reads of undeclared attributes now need a declaration or a cast. Following the plan's convention, fixes cast at the point of use and keep today's values; no runtime narrowing was added. Display fields in particular feed templates, so their value getters cast rather than filter.

The consumer casts land first, per area (fields, work packages, other features and plugin modules, one spec), while the index is still any, so every commit compiles; the last commit flips the signature. The payload helper's currentSchema || schema becomes ??; the value is a schema resource or nullish, so the result is unchanged.

The status dropdown's form callback is typed and its promise marked with void.

Stacked on #25660, whose resource, link and schema types this builds on. The field, query, work package view and remaining-module tickets follow this one.

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

  • Added/updated tests
  • Added/updated documentation in Lookbook (patterns, previews, etc)
  • Tested major browsers (Chrome, Firefox, Edge, ...)

Browser JavaScript diff (4 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.

diff --git frontend/src/app/features/hal/schemas/hal-payload.helper.js
index 747f49a..da832be 100644
--- frontend/src/app/features/hal/schemas/hal-payload.helper.js
+++ frontend/src/app/features/hal/schemas/hal-payload.helper.js
@@ -36,7 +36,7 @@ export class HalPayloadHelper {
                 if (Array.isArray(resource[property])) {
                     payload[property] = resource[property].map((element) => {
                         if (element instanceof HalResource) {
-                            return this.extractPayloadFromSchema(element, element.currentSchema || element.schema);
+                            return this.extractPayloadFromSchema(element, (element.currentSchema ?? element.schema));
                         }
                         return element;
                     });
diff --git frontend/src/app/features/work-packages/components/wp-query/url-params-helper.js
index 4478eed..55e0aed 100644
--- frontend/src/app/features/work-packages/components/wp-query/url-params-helper.js
+++ frontend/src/app/features/work-packages/components/wp-query/url-params-helper.js
@@ -225,8 +225,9 @@ let UrlParamsHelperService = class UrlParamsHelperService {
         if (query.columns) {
             return query.columns.map((column) => column.id || idFromLink(column.href));
         }
-        if (query._links.columns) {
-            return query._links.columns.map((column) => idFromLink(column.href));
+        const links = query._links;
+        if (links.columns) {
+            return links.columns.map((column) => idFromLink(column.href));
         }
         return [];
     }
diff --git frontend/src/app/shared/components/fields/display/field-types/work-package-display-field.module.js
index 74aac84..e66193d 100644
--- frontend/src/app/shared/components/fields/display/field-types/work-package-display-field.module.js
+++ frontend/src/app/shared/components/fields/display/field-types/work-package-display-field.module.js
@@ -23,7 +23,7 @@ export class WorkPackageDisplayField extends DisplayField {
         if (this.value.$loaded) {
             return this.value.id;
         }
-        return this.value.href.match(/(\d+)$/)[0];
+        return /(\d+)$/.exec(this.value.href)[0];
     }
     get wpRoutingId() {
         const linkedWp = this.value;
diff --git frontend/src/app/shared/components/op-context-menu/handlers/wp-status-dropdown-menu.directive.js
index 71a2163..9783109 100644
--- frontend/src/app/shared/components/op-context-menu/handlers/wp-status-dropdown-menu.directive.js
+++ frontend/src/app/shared/components/op-context-menu/handlers/wp-status-dropdown-menu.directive.js
@@ -21,7 +21,7 @@ let WorkPackageStatusDropdownDirective = class WorkPackageStatusDropdownDirectiv
     }
     open(evt) {
         const change = this.halEditing.changeFor(this.workPackage);
-        change.getForm().then((form) => {
+        void change.getForm().then((form) => {
             const statuses = form.schema.status.allowedValues;
             this.buildItems(statuses);
             const { writable } = change.schema.status;

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Copilot review overview

🟡 Changes recommended

An unused CollectionResource import causes the frontend ESLint check to fail.

Review effort: Balanced
Findings: 1 High severity

Open (1)
What changed in this PR

Replaces HalResource’s unsafe any index signature with unknown and updates consumers with explicit types while preserving runtime behavior.

Changes:

  • Changes undeclared HAL attributes from any to unknown.
  • Adds resource declarations and point-of-use casts across frontend features.
  • Updates schema typings and related HAL tests.
File Description
modules/​gitlab_integration/​frontend/​module/​tab-mrs/​wp-gitlab-mrs.service.ts Types merge-request resources.
modules/​gitlab_integration/​frontend/​module/​tab-issue/​wp-gitlab-issue.service.ts Types issue resources.
modules/​costs/​frontend/​module/​wp-display/​currency-display-field.module.ts Types currency values.
modules/​costs/​frontend/​module/​wp-display/​costs-by-type-display-field.module.ts Types cost collections and links.
frontend/​src/​app/​shared/​components/​op-context-menu/​wp-context-menu/​wp-single-context-menu.ts Types project identifiers.
frontend/​src/​app/​shared/​components/​op-context-menu/​handlers/​wp-status-dropdown-menu.directive.ts Types status schemas and values.
frontend/​src/​app/​shared/​components/​op-context-menu/​handlers/​wp-create-settings-menu.directive.ts Types custom-field links.
frontend/​src/​app/​shared/​components/​op-context-menu/​handlers/​op-types-context-menu.directive.ts Types allowed work-package types.
frontend/​src/​app/​shared/​components/​modals/​share-modal/​query-sharing-form.component.ts Types public-query schema access.
frontend/​src/​app/​shared/​components/​modals/​editor/​macro-wp-button-modal/​wp-button-macro.modal.ts Types available work-package types.
frontend/​src/​app/​shared/​components/​grids/​widgets/​documents/​documents.component.ts Types document attributes.
frontend/​src/​app/​shared/​components/​grids/​widgets/​custom-text/​custom-text.component.ts Types raw custom text.
frontend/​src/​app/​shared/​components/​grids/​widgets/​custom-text/​custom-text-edit-field.service.ts Types attachment links.
frontend/​src/​app/​shared/​components/​fields/​edit/​services/​hal-resource-editing.service.ts Types lock versions.
frontend/​src/​app/​shared/​components/​fields/​edit/​field/​editable-attribute-field.component.ts Uses schema-proxy typing.
frontend/​src/​app/​shared/​components/​fields/​display/​field-types/​wp-id-display-field.module.ts Types displayed work-package IDs.
frontend/​src/​app/​shared/​components/​fields/​display/​field-types/​work-package-display-field.module.ts Types linked work packages.
frontend/​src/​app/​shared/​components/​fields/​display/​field-types/​resources-display-field.module.ts Types multi-resource values.
frontend/​src/​app/​shared/​components/​fields/​display/​field-types/​resource-display-field.module.ts Types single-resource values.
frontend/​src/​app/​shared/​components/​fields/​display/​field-types/​project-status-display-field.module.ts Types project statuses.
frontend/​src/​app/​shared/​components/​fields/​display/​field-types/​plain-formattable-display-field.module.ts Types formattable values.
frontend/​src/​app/​shared/​components/​fields/​display/​field-types/​multiple-lines-user-display-field.module.ts Types user arrays.
frontend/​src/​app/​shared/​components/​fields/​display/​field-types/​linked-work-package-display-field.module.ts Narrows linked work-package IDs.
frontend/​src/​app/​shared/​components/​fields/​display/​field-types/​integer-display-field.module.ts Types integer input values.
frontend/​src/​app/​shared/​components/​fields/​display/​field-types/​hours-duration-display-field.module.ts Types hour durations.
frontend/​src/​app/​shared/​components/​fields/​display/​field-types/​highlighted-resource-display-field.module.ts Types highlighted resources.
frontend/​src/​app/​shared/​components/​fields/​display/​field-types/​formattable-display-field.module.ts Types rich-text values.
frontend/​src/​app/​shared/​components/​fields/​display/​field-types/​float-display-field.module.ts Types numeric values.
frontend/​src/​app/​shared/​components/​fields/​display/​field-types/​days-duration-display-field.module.ts Types day durations.
frontend/​src/​app/​shared/​components/​fields/​display/​field-types/​datetime-display-field.module.ts Types datetime values.
frontend/​src/​app/​shared/​components/​fields/​display/​field-types/​date-display-field.module.ts Types dates and statuses.
frontend/​src/​app/​shared/​components/​fields/​display/​field-types/​combined-date-display.field.ts Types combined dates.
frontend/​src/​app/​shared/​components/​fields/​display/​display-field.module.ts Types default display values.
frontend/​src/​app/​shared/​components/​fields/​display/​display-field.component.ts Uses schema-proxy typing.
frontend/​src/​app/​shared/​components/​fields/​display/​display-field-renderer.ts Propagates schema-proxy types.
frontend/​src/​app/​shared/​components/​editor/​components/​ckeditor-augmented-textarea/​ckeditor-augmented-textarea.component.ts Types attachment access.
frontend/​src/​app/​shared/​components/​autocompleter/​time-entries-work-package-autocompleter/​time-entries-work-package-autocompleter.component.ts Types linked work packages.
frontend/​src/​app/​features/​work-packages/​routing/​wp-view-base/​work-package-single-view.base.ts Types project identifiers.
frontend/​src/​app/​features/​work-packages/​components/​wp-table/​timeline/​container/​wp-timeline-container.directive.ts Types grouped resources.
frontend/​src/​app/​features/​work-packages/​components/​wp-table/​timeline/​cells/​timeline-milestone-cell-renderer.ts Types milestone schemas.
frontend/​src/​app/​features/​work-packages/​components/​wp-table/​timeline/​cells/​timeline-cell-renderer.ts Types date schemas.
frontend/​src/​app/​features/​work-packages/​components/​wp-table/​configuration-modal/​tabs/​highlighting-tab.component.ts Types highlight attributes.
frontend/​src/​app/​features/​work-packages/​components/​wp-single-view/​wp-single-view.component.ts Types schema context values.
frontend/​src/​app/​features/​work-packages/​components/​wp-relations/​wp-relations-create/​wp-relations-autocomplete/​wp-relations-autocomplete.component.ts Types relation-candidate links.
frontend/​src/​app/​features/​work-packages/​components/​wp-query/​url-params-helper.ts Types query HAL links.
frontend/​src/​app/​features/​work-packages/​components/​wp-fast-table/​builders/​modes/​grouped/​group-sums-builder.ts Types sum schemas.
frontend/​src/​app/​features/​work-packages/​components/​wp-card-view/​wp-single-card/​wp-single-card.component.ts Types card highlight resources.
frontend/​src/​app/​features/​work-packages/​components/​wp-breadcrumb/​wp-breadcrumb.component.ts Types breadcrumb identifiers.
frontend/​src/​app/​features/​work-packages/​components/​wp-breadcrumb/​wp-breadcrumb-parent.component.ts Types parent identifiers.
frontend/​src/​app/​features/​work-packages/​components/​filters/​query-filters/​query-filters.component.ts Types templated-filter access.
frontend/​src/​app/​features/​work-packages/​components/​filters/​filter-integer-value/​filter-integer-value.component.ts Types filter schema values.
frontend/​src/​app/​features/​team-planner/​team-planner/​planner/​team-planner.component.ts Types assignee schemas.
frontend/​src/​app/​features/​team-planner/​team-planner/​calendar-drag-drop.service.ts Types work-package durations.
frontend/​src/​app/​features/​hal/​schemas/​hal-payload.helper.ts Types nested schemas and uses nullish fallback.
frontend/​src/​app/​features/​hal/​resources/​work-package-resource.ts Declares links and types work-package attributes.
frontend/​src/​app/​features/​hal/​resources/​query-form-resource.ts Uses form-schema typing.
frontend/​src/​app/​features/​hal/​resources/​hal-resource.ts Changes index signature to unknown.
frontend/​src/​app/​features/​hal/​resources/​hal-resource.spec.ts Updates tests for unknown attributes.
frontend/​src/​app/​features/​hal/​resources/​grid-widget-resource.ts Declares widget creation state.
frontend/​src/​app/​features/​hal/​resources/​form-resource.ts Declares custom-field link.
frontend/​src/​app/​features/​bim/​bcf/​helper/​viewpoints.service.ts Types BCF conversion payload.
frontend/​src/​app/​core/​global_search/​input/​global-search-input.component.ts Narrows search-option items.
frontend/​src/​app/​core/​apiv3/​endpoints/​grids/​apiv3-grid-form.ts Types grid payload extraction.

💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.

@myabc
myabc force-pushed the implementation/op-20349-eslint-typing-fix-hal-index-signature branch from ac71ea0 to 2705f0a Compare September 29, 2026 09:47
@myabc myabc added eslint javascript Pull requests that update Javascript code needs review labels Sep 29, 2026
@myabc myabc added this to the 18.0.x milestone Sep 29, 2026
@github-actions

Copy link
Copy Markdown

Warning

Flaky specs

  • rspec ./modules/bim/spec/features/bim_filter_spec.rb[1:1:1]
  • rspec ./spec/features/workflows/edit_multi_role_spec.rb[1:4:4:2]
🤖 Ask Copilot to investigate

Copy 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.

@copilot The following spec(s) are flaky in CI (first seen on PR #25661, linked for reference only):

- `rspec ./modules/bim/spec/features/bim_filter_spec.rb[1:1:1]`
- `rspec ./spec/features/workflows/edit_multi_role_spec.rb[1:4:4:2]`

Treat this as a standalone task, unrelated to PR #25661. Create a new branch from origin/dev and open a new pull request targeting dev — do not stack it on PR #25661 or reuse that branch.

Follow the playbook in docs/development/testing/handling-flaky-tests/README.md to find the root cause and fix the underlying race — do not skip, delete, or weaken the spec to make it pass; disabling is a last resort per the playbook, and only with a bug ticket. Verify the fix by running the spec(s) repeatedly (e.g. `script/bulk_run_rspec --run-count 10`).

If you cannot reproduce the flake or are not confident in a fix after reasonable investigation, do not fabricate a change or skip the spec to force CI green. Instead, leave the pull request in draft and document what you tried, the suspected cause, and any leads in its description, then assign @myabc to take over.

Once the fix is verified, title the PR after the spec(s) it fixes, and use the PR description to explain the root cause, how the change resolves it, and the before/after results. Label the PR `flaky-spec`, assign @myabc, and request a review from @myabc.
On every commit, set @myabc as the sole co-author with a `Co-authored-by:` trailer (use their GitHub no-reply email so it links to their account), so it is traceable who dispatched the fix.

@github-actions github-actions Bot added the ai: Directed 🪄 A human specified the requirements and AI implemented most of it; They validated via testing. label Sep 29, 2026
Casts the resource attributes that display and edit fields read by
name at the point of use, keeping today's values. These reads compiled
only because HalResource declared an any index signature, which the
next commit removes.

https://community.openproject.org/wp/OP-20349
Casts work package attributes and raw links that views and services
read without a declared type, keeping today's behaviour, ahead of the
HalResource index signature becoming unknown.

https://community.openproject.org/wp/OP-20349
Casts undeclared resource attributes read in grids, context menus,
modals, search, BCF, the team planner and the costs and GitLab plugin
modules, keeping today's behaviour, ahead of the HalResource index
signature becoming unknown.

https://community.openproject.org/wp/OP-20349
Casts the resource attribute a spec reads by name, since specs are
typechecked even though they are exempt from the typing lint rules.

https://community.openproject.org/wp/OP-20349
Declares the HalResource index signature as unknown instead of any.
The any signature let every undeclared attribute read skip type
checking, which a code comment had tracked as a source of bugs since
2019 (#31462). Reads of undeclared attributes now need a declaration
or a cast, as the preceding commits add.

The payload helper picks the current schema with ?? instead of ||; the
value is a schema resource or nullish, so the result is unchanged.

https://community.openproject.org/wp/OP-20349
@myabc
myabc force-pushed the implementation/op-20349-eslint-typing-fix-hal-index-signature branch from 2705f0a to cdd9389 Compare September 29, 2026 18:29
@github-actions

Copy link
Copy Markdown

Warning

Flaky specs

  • rspec ./spec/features/roles/report_spec.rb[1:1]
  • rspec ./spec/features/roles/report_spec.rb[1:3]
🤖 Ask Copilot to investigate

Copy 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.

@copilot The following spec(s) are flaky in CI (first seen on PR #25661, linked for reference only):

- `rspec ./spec/features/roles/report_spec.rb[1:1]`
- `rspec ./spec/features/roles/report_spec.rb[1:3]`

Treat this as a standalone task, unrelated to PR #25661. Create a new branch from origin/dev and open a new pull request targeting dev — do not stack it on PR #25661 or reuse that branch.

Follow the playbook in docs/development/testing/handling-flaky-tests/README.md to find the root cause and fix the underlying race — do not skip, delete, or weaken the spec to make it pass; disabling is a last resort per the playbook, and only with a bug ticket. Verify the fix by running the spec(s) repeatedly (e.g. `script/bulk_run_rspec --run-count 10`).

If you cannot reproduce the flake or are not confident in a fix after reasonable investigation, do not fabricate a change or skip the spec to force CI green. Instead, leave the pull request in draft and document what you tried, the suspected cause, and any leads in its description, then assign @myabc to take over.

Once the fix is verified, title the PR after the spec(s) it fixes, and use the PR description to explain the root cause, how the change resolves it, and the before/after results. Label the PR `flaky-spec`, assign @myabc, and request a review from @myabc.
On every commit, set @myabc as the sole co-author with a `Co-authored-by:` trailer (use their GitHub no-reply email so it links to their account), so it is traceable who dispatched the fix.

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

ai: Directed 🪄 A human specified the requirements and AI implemented most of it; They validated via testing. eslint javascript Pull requests that update Javascript code needs review

Development

Successfully merging this pull request may close these issues.

2 participants