Skip to content

fix(endpoint): Make Entity classes assignable to EntityInterface - #4149

Merged
ntucker merged 7 commits into
masterfrom
claude/project-thread-9j2et9
Oct 4, 2026
Merged

ntucker merged 7 commits into
masterfrom
claude/project-thread-9j2et9

Conversation

@ntucker

@ntucker ntucker commented Oct 4, 2026 •

Copy link
Copy Markdown
Collaborator

Requested by Nathaniel · project thread

Follow-up to #4130.

Motivation

Entity's static pk() declared args?: any[], but EntityInterface.pk takes readonly any[], so under strictFunctionTypes an Entity class wasn't assignable to EntityInterface.

import type { EntityInterface } from '@data-client/react';

// Before: TypeScript error (args is readonly in EntityInterface)
// After: typechecks
const schema: EntityInterface = User;

Solution

  • Entity.pk and IEntityClass.pk (EntityMixin) take args?: readonly any[]. Entity.pk is declared with method syntax, so existing subclass overrides that annotate args?: any[] keep compiling.
  • Batch controller.set() types use EntityInterface instead of the private EntityLike. Batch set() is unreleased (v0.19), so the only effect is that it needs @data-client/rest/endpoint/graphql from the same release; noted in its changeset.
  • Endpoint's own EntityInterface keeps mutable args, so classes implementing it are unaffected.

Nothing already released breaks. Verified with yarn build:types (incl. legacy ts* outputs) and tsc -p tsconfig.test.json; Entity.test.ts covers assignability and a mutable-args override.

🤖 Generated with Claude Code

https://claude.ai/code/session_01F92DVcdVpL6sVLSA69App5


Note

Low Risk
Type-only compatibility shims with no runtime behavior change; main follow-up is aligning package versions for batch set() typings and eventual removal of pk() shims.

Overview
Aligns Entity static pk() with EntityInterface by typing the args parameter as readonly any[], so assignments like const schema: EntityInterface = User typecheck under strictFunctionTypes.

A method-syntax declaration on Entity.pk keeps existing static pk() overrides that still annotate args as a mutable array compiling for now; docs and changesets steer users toward readonly any[] before a future cleanup tracked in plans/next-breaking-release.md. Maintainer guidance for shipping non-breaking type fixes is added in .cursor/rules/breaking-changes.mdc.

Batch controller.set([Entity], rows) typings in core now use EntityInterface instead of a local EntityLike helper, so batch set() expects matching @data-client/endpoint/rest/graphql from the same release. Release notes, changesets, and the v0.19 blog cover the assignability fix and pk() migration tip; tests cover EntityInterface assignability and mutable-args overrides.

Reviewed by Cursor Bugbot for commit 2da5e5c. Bugbot is set up for automated code reviews on this repo. Configure here.

@changeset-bot

changeset-bot Bot commented Oct 4, 2026 •

Copy link
Copy Markdown

🦋 Changeset detected

Latest commit: 2da5e5c

The changes in this PR will be included in the next version bump.

This PR includes changesets to release 13 packages
Name Type
@data-client/core Patch
@data-client/react Patch
@data-client/vue Patch
@data-client/endpoint Patch
@data-client/rest Patch
@data-client/graphql Patch
example-benchmark Patch
example-benchmark-react Patch
test-bundlesize Patch
coinbase-lite Patch
normalizr-github-example Patch
normalizr-redux-example Patch
normalizr-relationships Patch

Not sure what this means? Click here to learn what changesets are.

Click here if you're a maintainer who wants to add another changeset to this PR

@vercel

vercel Bot commented Oct 4, 2026 •

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

1 Skipped Deployment
Project Deployment Actions Updated
docs-site Ignored Ignored Preview Oct 4, 2026 4:49pm UTC

Request Review

@ntucker ntucker self-assigned this Oct 4, 2026

@ntucker ntucker left a comment

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

CHANGE_THIS_PR: finish the readonly fix in the TS 4.0 type overlays.

lib / modern path looks right: Entity.pk, IEntityClass.pk, and endpoint EntityInterface.pk all take readonly any[], so Entity classes assign under strictFunctionTypes, and dropping EntityLike is the right cleanup after #4130.

packages/endpoint/src-4.0-types/schemas/Entity.d.ts and EntityTypes.d.ts still declare class/static pk with mutable args?: any[]. scripts/build-legacy-types.sh applies those overlays after downlevel-dts from lib, and typesVersions still sends TS 4.0–4.1 to ts4.0/. There is no interface.d.ts overlay, and normalizr’s EntityInterface.pk is already readonly any[], so that path keeps the same assignability hole this PR closes for everyone else. Instance pk in those same overlays already uses readonly any[]; the overlay’s reason is only removing abstract new.

Please change those two args to readonly any[] (same one-line edits as the source). That completes the fix without a redesign.

@codecov

codecov Bot commented Oct 4, 2026 •

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 98.01%. Comparing base (c3ce245) to head (2da5e5c).

Additional details and impacted files
@@           Coverage Diff           @@
##           master    #4149   +/-   ##
=======================================
  Coverage   98.01%   98.01%           
=======================================
  Files         160      160           
  Lines        3127     3127           
  Branches      622      622           
=======================================
  Hits         3065     3065           
  Misses         18       18           
  Partials       44       44           

☔ View full report in Codecov by Harness.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

ntucker commented Oct 4, 2026

Copy link
Copy Markdown
Collaborator Author

Confirmed: build:legacy-types:4.0 copies src-4.0-types over the downleveled output, so ts4.0/ still had mutable args on static pk. Fixed in 64a0af7 (both overlays now readonly any[]); rebuilt ts4.0/ and no mutable pk args remain. process() args stay mutable, matching the source.


Generated by Claude Code

ntucker commented Oct 4, 2026

Copy link
Copy Markdown
Collaborator Author

Lead Engineer: The TypeScript 4.0 overlay is already readonly on the current tip. No new commit.

#4149 (review) was against 00ecd7c0. On that commit, packages/endpoint/src-4.0-types/schemas/Entity.d.ts (static pk) and EntityTypes.d.ts (IEntityClass.pk) still took args?: any[]. HEAD is now 64a0af792157bdd1b0626c3bb084b6bf25baa68d (fix(endpoint): Make pk() args readonly in TS 4.0 type overlays), whose parent is that SHA. Both signatures are args?: readonly any[].

Motivation: typesVersions sends TypeScript 4.0–4.1 to those overlays, so a mutable args array there reopens the assignability hole the modern path closed. Finding: the overlay commit landed after the reviewed SHA, so I did not push another change.

Base automatically changed from claude/project-thread-fjrb2j to master October 4, 2026 15:12
Entity classes now satisfy normalizr's EntityInterface, so Controller
drops its private EntityLike workaround. Endpoint's own EntityInterface
also takes readonly args, so both contracts match.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01F92DVcdVpL6sVLSA69App5
@ntucker
ntucker force-pushed the claude/project-thread-9j2et9 branch from 64a0af7 to b50f6c5 Compare October 4, 2026 16:20
@github-actions

github-actions Bot commented Oct 4, 2026

Copy link
Copy Markdown
Contributor

Size Change: 0 B

Total Size: 103 kB

ℹ️ View Unchanged
Filename Size
examples/test-bundlesize/dist/App.js 1.46 kB
examples/test-bundlesize/dist/polyfill.js 307 B
examples/test-bundlesize/dist/rdcClient.js 10.9 kB
examples/test-bundlesize/dist/rdcEndpoint.js 8.07 kB
examples/test-bundlesize/dist/rdcNextjs.js 12.3 kB
examples/test-bundlesize/dist/rdcPipeableStream.js 9.63 kB
examples/test-bundlesize/dist/react.js 59.7 kB
examples/test-bundlesize/dist/webpack-runtime.js 784 B

compressed-size-action

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01F92DVcdVpL6sVLSA69App5

@github-actions github-actions Bot 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.

Benchmark React

Details
Benchmark suite Current: 2da5e5c Previous: b4b502d Ratio
data-client: getlist-100 131.58 ops/s (± 4.6%) 212.77 ops/s (± 5.7%) 1.62
data-client: getlist-500 41.5 ops/s (± 5.2%) 63.7 ops/s (± 5.7%) 1.53
data-client: update-entity 327.96 ops/s (± 8.5%) 465.37 ops/s (± 8.5%) 1.42
data-client: update-user 322.58 ops/s (± 8.2%) 454.55 ops/s (± 6.8%) 1.41
data-client: getlist-500-sorted 42.31 ops/s (± 10.1%) 63.9 ops/s (± 7.9%) 1.51
data-client: update-entity-sorted 277.78 ops/s (± 6.5%) 408.33 ops/s (± 6.2%) 1.47
data-client: update-entity-multi-view 303.03 ops/s (± 7.6%) 444.66 ops/s (± 7.2%) 1.47
data-client: list-detail-switch-10 7.69 ops/s (± 7.6%) 20.2 ops/s (± 10.0%) 2.63
data-client: update-user-10000 72.46 ops/s (± 12.4%) 114.29 ops/s (± 13.5%) 1.58
data-client: invalidate-and-resolve 36.56 ops/s (± 6.0%) 58.49 ops/s (± 5.5%) 1.60
data-client: unshift-item 144.93 ops/s (± 12.5%) 294.12 ops/s (± 5.1%) 2.03
data-client: delete-item 270.27 ops/s (± 2.4%) 416.67 ops/s (± 4.3%) 1.54
data-client: move-item 170.95 ops/s (± 7.7%) 243.9 ops/s (± 8.8%) 1.43

This comment was automatically generated by workflow using github-action-benchmark.

@github-actions github-actions Bot 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.

Benchmark

Details
Benchmark suite Current: 2da5e5c Previous: b4b502d Ratio
normalizeLong 432 ops/sec (±3.12%) 435 ops/sec (±4.05%) 1.01
normalizeLong Values 394 ops/sec (±0.41%) 396 ops/sec (±1.28%) 1.01
normalizeLong Scalar 363 ops/sec (±3.16%) 371 ops/sec (±3.34%) 1.02
normalizeLong Scalar update 893 ops/sec (±0.55%) 901 ops/sec (±0.24%) 1.01
denormalizeLong 223 ops/sec (±6.06%) 227 ops/sec (±6.64%) 1.02
denormalizeLong Values 211 ops/sec (±4.77%) 219 ops/sec (±4.15%) 1.04
denormalizeLong donotcache 991 ops/sec (±0.52%) 1004 ops/sec (±0.68%) 1.01
denormalizeLong Values donotcache 731 ops/sec (±0.56%) 741 ops/sec (±0.15%) 1.01
denormalizeLong Scalar donotcache 1040 ops/sec (±0.47%) 1034 ops/sec (±0.57%) 0.99
denormalizeShort donotcache 500x 1267 ops/sec (±1.11%) 1383 ops/sec (±0.14%) 1.09
denormalizeShort 500x 585 ops/sec (±6.47%) 628 ops/sec (±7.30%) 1.07
denormalizeShort 500x withCache 5920 ops/sec (±0.37%) 6544 ops/sec (±0.57%) 1.11
queryShort 500x withCache 3126 ops/sec (±0.73%) 3096 ops/sec (±2.95%) 0.99
buildQueryKey All 57986 ops/sec (±0.94%) 59202 ops/sec (±1.22%) 1.02
query All withCache 5648 ops/sec (±2.63%) 6282 ops/sec (±2.76%) 1.11
denormalizeLong with mixin Entity 206 ops/sec (±5.57%) 213 ops/sec (±8.11%) 1.03
denormalizeLong withCache 5746 ops/sec (±3.90%) 6436 ops/sec (±0.36%) 1.12
denormalizeLong withCache (Scalar churn) 7063 ops/sec (±0.46%) 6429 ops/sec (±0.70%) 0.91
denormalizeLong Values withCache 5158 ops/sec (±1.67%) 5157 ops/sec (±1.10%) 1.00
denormalizeLong Scalar withCache 7485 ops/sec (±1.13%) 7840 ops/sec (±0.38%) 1.05
denormalizeLong Scalar update withCache 4047 ops/sec (±0.73%) 4086 ops/sec (±0.46%) 1.01
denormalizeLong All withCache 6330 ops/sec (±0.30%) 6426 ops/sec (±0.24%) 1.02
denormalizeLong Query-sorted withCache 6144 ops/sec (±1.24%) 6538 ops/sec (±1.91%) 1.06
denormalizeLongAndShort withEntityCacheOnly 1441 ops/sec (±0.47%) 1769 ops/sec (±0.30%) 1.23
denormalize bidirectional 50 4391 ops/sec (±10.60%) 4565 ops/sec (±7.95%) 1.04
denormalize bidirectional 50 donotcache 41969 ops/sec (±0.35%) 41869 ops/sec (±0.22%) 1.00
getResponse 4260 ops/sec (±2.55%) 4469 ops/sec (±3.49%) 1.05
getResponse (null) 7294192 ops/sec (±0.82%) 10408471 ops/sec (±0.70%) 1.43
getResponse (clear cache) 192 ops/sec (±8.26%) 210 ops/sec (±7.15%) 1.09
getSmallResponse 3329 ops/sec (±1.51%) 3553 ops/sec (±1.61%) 1.07
getSmallInferredResponse 2689 ops/sec (±0.20%) 2886 ops/sec (±0.13%) 1.07
getResponse Collection 4328 ops/sec (±3.84%) 4488 ops/sec (±3.44%) 1.04
get Collection 2718 ops/sec (±0.72%) 3804 ops/sec (±0.22%) 1.40
get Query-sorted 5024 ops/sec (±1.93%) 4955 ops/sec (±1.97%) 0.99
setLong 434 ops/sec (±0.87%) 434 ops/sec (±0.27%) 1
setLongWithMerge 249 ops/sec (±0.43%) 252 ops/sec (±0.95%) 1.01
setLongWithSimpleMerge 266 ops/sec (±0.31%) 268 ops/sec (±0.51%) 1.01
setSmallResponse 500x 849 ops/sec (±1.51%) 909 ops/sec (±1.44%) 1.07
setMany 50x one-per-row 142 ops/sec (±0.54%) 156 ops/sec (±0.22%) 1.10
setMany 50 batch 3417 ops/sec (±0.80%) 3655 ops/sec (±0.58%) 1.07
setMany 500x one-per-row 14.36 ops/sec (±0.76%) 15.95 ops/sec (±0.39%) 1.11
setMany 500 batch 1366 ops/sec (±3.22%) 1422 ops/sec (±0.16%) 1.04

This comment was automatically generated by workflow using github-action-benchmark.

Declare static pk() with method syntax so subclass overrides that type
args as a mutable array still compile. Revert endpoint EntityInterface
and keep core's loose EntityLike so mixed package versions keep working.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01F92DVcdVpL6sVLSA69App5
@ntucker ntucker changed the title fix(endpoint): Make Entity.pk() args readonly to match EntityInterface fix(endpoint): Make Entity classes assignable to EntityInterface Oct 4, 2026
claude added 4 commits October 4, 2026 16:43
Entity now satisfies EntityInterface, so the private EntityLike type is
no longer needed. Batch set() is unreleased, so this only requires
matching endpoint versions for a new feature.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01F92DVcdVpL6sVLSA69App5
Add plans/next-breaking-release.md for the compatibility shims, and note
in the v0.19 blog and changeset that static pk() overrides should type
args as readonly ahead of that release.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01F92DVcdVpL6sVLSA69App5
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01F92DVcdVpL6sVLSA69App5
@ntucker
ntucker merged commit cc57a77 into master Oct 4, 2026
29 checks passed
@ntucker
ntucker deleted the claude/project-thread-9j2et9 branch October 4, 2026 16:55
@github-actions github-actions Bot mentioned this pull request Oct 4, 2026
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.

2 participants