Skip to content

fix(types): Fix endpoint/rest legacy type errors with skipLibCheck off - #4140

Merged
ntucker merged 8 commits into
claude/project-thread-bw2hx0from
claude/project-thread-g6z5kd
Oct 4, 2026
Merged

ntucker merged 8 commits into
claude/project-thread-bw2hx0from
claude/project-thread-g6z5kd

Conversation

@ntucker

@ntucker ntucker commented Oct 4, 2026 •

Copy link
Copy Markdown
Collaborator

Requested by Nathaniel · project thread

Follow-up from #4138 (based on #4122's branch; retargets to master once #4122 merges).

Motivation

Before: with skipLibCheck: false, @data-client/endpoint and @data-client/rest declarations reported errors on TypeScript 4.0–4.5, and on 4.0/4.1 an Entity couldn't be an Endpoint schema (stale 4.0 overlays). CI never noticed because every typetest uses skipLibCheck: true.

After: no errors on 4.0, 4.1, 4.2, 4.3, 4.4, 4.5, 4.7, 4.8, 4.9, 5.0, 5.3, 5.4, and CI now fails if a legacy output regresses.

import { Endpoint, Entity, schema } from '@data-client/endpoint';

class User extends Entity {
  id = '';
  type = 'users';
}
const getUser = new Endpoint(
  (id: string) => fetch(`/users/${id}`).then(res => res.json()),
  { schema: User },
);
const feed = new schema.Union({ users: User }, 'type');

// Before (TypeScript 4.0, skipLibCheck: false):
//   error TS2456: Type alias 'RemoveArray' circularly references itself.
//   error TS2322: Type 'typeof User' is not assignable to type 'EntityInterface<any>'.
// Before (TypeScript 4.2):
//   error TS2344: Type 'TBase' does not satisfy the constraint 'new (...args: any) => any'.
// After: no errors

Solution

  • TS 4.0 (no recursive conditional types): RemoveArray and PartialArray move to their own tupleTypes.ts module (PartialArray is still re-exported from utility), so the 4.0 overlay src-4.0-types/tupleTypes.d.ts replaces just those two types, like NoInfer.ts does. RemoveArray is unrolled up to 3 bound args (bind() still types the remaining params); PartialArray is approximated with Partial.
  • TS < 4.2 stale overlays: src-4.0-types/schemas/{Entity,EntityTypes}.d.ts were hand copies whose only change was abstract new → new, and had drifted (old normalize/denormalize signatures). build-legacy-types.sh now does that rewrite on the downleveled output for versions below 4.2 (downlevel-dts misses it, 0.11 included), so those copies are deleted and can't go stale again. ts3.4 output improves too.
  • TS 4.2 (lib InstanceType only accepts concrete new): EntityTypes/EntityMixin use a new internal ConstructorInstance<>, identical to the 4.3+ lib InstanceType. (AbstractInstanceType<> isn't a drop-in: it resolves constructor type aliases to any.)
  • TS 4.2–4.4: UnionInstance's Args drops its EntityFields constraint (only used for queryKey args; the default is unchanged).
  • TS 4.0–4.5 (found while verifying, not in the original list): RestEndpoint.getPage passes Extract<O['paginationField'], string>, since those versions don't narrow O['paginationField'] in the true branch.
  • CI: esmodule-types (4.0, 4.1, 4.3, 4.8, 5.3) also runs examples/todo-app/tsconfig.typetest-libcheck.json, a standalone consumer of endpoint/normalizr/rest with skipLibCheck: false and types: [] (so @types/react etc. stay out of it). Checked that it fails on fix(types): Apply newer legacy type overlays to older TS outputs #4138's outputs (4.0, 4.1, 4.3) and passes here. ci-config.mdc updated.

Validated: root tsc --project tsconfig.test.json, lint, endpoint/rest/normalizr/graphql unit tests (one EntityMixin.test.ts @ts-expect-error placement fails locally with and without this change; CI is green).

Open questions

TS 3.x (ts3.4/, untested in CI) still reports pre-existing SnapshotInterface.d.ts rest-element errors; not addressed here.

🤖 Generated with Claude Code

https://claude.ai/code/session_01AMEHZGboXdMFRkF4D2ktki

claude added 3 commits October 4, 2026 02:43
endpoint and normalizr legacy outputs (ts4.2, ts4.0, ts3.4) now inherit
every newer version's src-*-types overlay, as rest already did. This
replaces the TS 5.4 NoInfer builtin with the NI<T> = T fallback for
TS < 4.8 consumers.

normalizr re-exports memo types by name instead of `export type *`
(TS 5.0 syntax that downlevel-dts leaves as is).

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01MpCgcZt8G8R5KARZ7z5CKE
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01MpCgcZt8G8R5KARZ7z5CKE
- TS 4.0: unroll RemoveArray and approximate PartialArray (recursive
  conditional types need 4.1) in new src-4.0-types overlays
- TS <4.2: rewrite `abstract new` to `new` in the downleveled output and
  drop the stale src-4.0-types Entity/EntityTypes copies
- TS 4.2: ConstructorInstance<> replaces InstanceType<> on abstract TBase
- TS 4.2-4.4: UnionInstance Args is unconstrained
- TS 4.0-4.5: rest getPage narrows paginationField with Extract<>
- CI: esmodule-types also typechecks a skipLibCheck: false consumer

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

changeset-bot Bot commented Oct 4, 2026 •

Copy link
Copy Markdown

🦋 Changeset detected

Latest commit: ac19e20

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

This PR includes changesets to release 10 packages
Name Type
@data-client/endpoint Patch
@data-client/rest Patch
@data-client/graphql Patch
example-benchmark Patch
example-benchmark-react Patch
normalizr-github-example Patch
normalizr-redux-example Patch
normalizr-relationships Patch
test-bundlesize Patch
coinbase-lite 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

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01AMEHZGboXdMFRkF4D2ktki
@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 3:22am UTC

Request Review

@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 97.87%. Comparing base (db068cd) to head (ac19e20).

Additional details and impacted files
@@                      Coverage Diff                      @@
##           claude/project-thread-bw2hx0    #4140   +/-   ##
=============================================================
  Coverage                         97.87%   97.87%           
=============================================================
  Files                               156      156           
  Lines                              3057     3057           
  Branches                            612      612           
=============================================================
  Hits                               2992     2992           
  Misses                               18       18           
  Partials                             47       47           

☔ 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
ntucker marked this pull request as ready for review October 4, 2026 03:04
Base automatically changed from claude/project-thread-mdbtb1 to claude/project-thread-bw2hx0 October 4, 2026 03:17
claude added 4 commits October 4, 2026 03:20
…one small file

- RemoveArray and PartialArray move to tupleTypes.ts; the src-4.0-types
  overlay replaces only that module instead of copying endpointTypes/utility
- Legacy build only rewrites files that contain `abstract new (`
- libcheck typetest skips TypeScript's own lib checks
- Split the legacy types bullet in ci-config.mdc

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01AMEHZGboXdMFRkF4D2ktki
…to claude/project-thread-g6z5kd

# Conflicts:
#	.cursor/rules/ci-config.mdc
#	scripts/build-legacy-types.sh
#	website/blog/2026-10-03-v0.19-batch-set.md
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01AMEHZGboXdMFRkF4D2ktki
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01AMEHZGboXdMFRkF4D2ktki
@ntucker
ntucker merged commit 350c124 into claude/project-thread-bw2hx0 Oct 4, 2026
22 checks passed
@ntucker
ntucker deleted the claude/project-thread-g6z5kd branch October 4, 2026 03:57
ntucker added a commit that referenced this pull request Oct 4, 2026
#4140)

* fix(types): Apply newer legacy type overlays to older TS outputs

endpoint and normalizr legacy outputs (ts4.2, ts4.0, ts3.4) now inherit
every newer version's src-*-types overlay, as rest already did. This
replaces the TS 5.4 NoInfer builtin with the NI<T> = T fallback for
TS < 4.8 consumers.

normalizr re-exports memo types by name instead of `export type *`
(TS 5.0 syntax that downlevel-dts leaves as is).

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

* docs(blog): Note legacy TypeScript types fix

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

* fix(types): Fix endpoint/rest legacy type errors with skipLibCheck off

- TS 4.0: unroll RemoveArray and approximate PartialArray (recursive
  conditional types need 4.1) in new src-4.0-types overlays
- TS <4.2: rewrite `abstract new` to `new` in the downleveled output and
  drop the stale src-4.0-types Entity/EntityTypes copies
- TS 4.2: ConstructorInstance<> replaces InstanceType<> on abstract TBase
- TS 4.2-4.4: UnionInstance Args is unconstrained
- TS 4.0-4.5: rest getPage narrows paginationField with Extract<>
- CI: esmodule-types also typechecks a skipLibCheck: false consumer

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

* docs(blog): Link #4140 in v0.19 notes

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

* refactor(types): Isolate recursive tuple types so the 4.0 overlay is one small file

- RemoveArray and PartialArray move to tupleTypes.ts; the src-4.0-types
  overlay replaces only that module instead of copying endpointTypes/utility
- Legacy build only rewrites files that contain `abstract new (`
- libcheck typetest skips TypeScript's own lib checks
- Split the legacy types bullet in ci-config.mdc

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

* internal: Resolve leftover merge conflict in ci-config rule

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

* internal: Resolve leftover merge conflict in build-legacy-types.sh

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

---------

Co-authored-by: Claude <noreply@anthropic.com>
ntucker added a commit that referenced this pull request Oct 4, 2026
…i): fail-open esmodule relevance, faster rest legacy types (#4122)

* internal(ci): Fail open on esmodule relevance and speed up rest legacy types

The esmodule relevance flag now uses a denylist of provably irrelevant
paths, so new inputs run the esmodule jobs by default. rest's legacy
types build uses scripts/build-legacy-types.sh (direct downlevel-dts,
d.ts-only copies) with --newer-overlays-last to keep its overlay order;
ts4.0/ts4.1 output is byte-identical (8.6s -> 4.5s locally).

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

* internal(ci): Simplify legacy types overlay order and share docs-only paths

Downlevel always runs first; --inherit-newer-overlays (rest) then applies
earlier versions' custom types. Without the flag those copies were always
overwritten by downlevel-dts, so endpoint/normalizr output is unchanged
(ts*/ byte-identical). The esmodule and tests relevance checks now share
one DOCS_ONLY path list.

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

* fix(types): Apply newer legacy type overlays to older TS outputs (#4138)

* fix(types): Apply newer legacy type overlays to older TS outputs

endpoint and normalizr legacy outputs (ts4.2, ts4.0, ts3.4) now inherit
every newer version's src-*-types overlay, as rest already did. This
replaces the TS 5.4 NoInfer builtin with the NI<T> = T fallback for
TS < 4.8 consumers.

normalizr re-exports memo types by name instead of `export type *`
(TS 5.0 syntax that downlevel-dts leaves as is).

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

* docs(blog): Note legacy TypeScript types fix

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

---------

Co-authored-by: Claude <noreply@anthropic.com>

* fix(types): Fix endpoint/rest legacy type errors with skipLibCheck off (#4140)

* fix(types): Apply newer legacy type overlays to older TS outputs

endpoint and normalizr legacy outputs (ts4.2, ts4.0, ts3.4) now inherit
every newer version's src-*-types overlay, as rest already did. This
replaces the TS 5.4 NoInfer builtin with the NI<T> = T fallback for
TS < 4.8 consumers.

normalizr re-exports memo types by name instead of `export type *`
(TS 5.0 syntax that downlevel-dts leaves as is).

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

* docs(blog): Note legacy TypeScript types fix

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

* fix(types): Fix endpoint/rest legacy type errors with skipLibCheck off

- TS 4.0: unroll RemoveArray and approximate PartialArray (recursive
  conditional types need 4.1) in new src-4.0-types overlays
- TS <4.2: rewrite `abstract new` to `new` in the downleveled output and
  drop the stale src-4.0-types Entity/EntityTypes copies
- TS 4.2: ConstructorInstance<> replaces InstanceType<> on abstract TBase
- TS 4.2-4.4: UnionInstance Args is unconstrained
- TS 4.0-4.5: rest getPage narrows paginationField with Extract<>
- CI: esmodule-types also typechecks a skipLibCheck: false consumer

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

* docs(blog): Link #4140 in v0.19 notes

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

* refactor(types): Isolate recursive tuple types so the 4.0 overlay is one small file

- RemoveArray and PartialArray move to tupleTypes.ts; the src-4.0-types
  overlay replaces only that module instead of copying endpointTypes/utility
- Legacy build only rewrites files that contain `abstract new (`
- libcheck typetest skips TypeScript's own lib checks
- Split the legacy types bullet in ci-config.mdc

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

* internal: Resolve leftover merge conflict in ci-config rule

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

* internal: Resolve leftover merge conflict in build-legacy-types.sh

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

---------

Co-authored-by: Claude <noreply@anthropic.com>

* internal(ci): Harden legacy types script and libcheck lib/ on latest TS

version_lt compares major.minor in bash instead of relying on sort -V
(a missing -V silently skipped the abstract new rewrite), and the
abstract new grep no longer fails the build under pipefail when nothing
matches. esmodule-types-latest also runs the skipLibCheck: false
typetest so lib/ is checked on the newest compiler.

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

---------

Co-authored-by: Claude <noreply@anthropic.com>
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