Skip to content

fix(rest): Keep custom endpoints in resource().extend({...}) result type - #4184

Open
ntucker wants to merge 3 commits into
masterfrom
claude/rest-extend-typing-ynra0f
Open

ntucker wants to merge 3 commits into
masterfrom
claude/rest-extend-typing-ynra0f

Conversation

@ntucker

@ntucker ntucker commented Oct 5, 2026 •

Copy link
Copy Markdown
Collaborator

Requested by Nathaniel · project thread

Motivation

Customizing a resource's standard endpoints with .extend({ ... }) kept every endpoint at runtime, but TypeScript lost the ones added before it with .extend('name', options), and the deprecated create.

const UserResource = resource({ path: '/users/:id', schema: User })
  .extend('current', { path: '/user' })
  .extend({ get: { dataExpiryLength: 60000 } });

// Before: Property 'current' does not exist
// After: no error
const me = await ctrl.fetch(UserResource.current);

Solution

The object form of Extendable.extend() now returns CustomResource<...> & Omit<R, keyof ResourceInterface | 'extend'> (also in the TS 4.0/4.1 legacy declaration). Omit is used inline rather than a new helper alias so declaration emit in user code (declaration: true) can still name the type.

New typescript-tests/extendOptions.test.ts covers this, plus regression tests that extend() options are type-checked (dataExpiryLength: 'long' etc.) across RestEndpoint.extend(), resource().extend('key', ...) and resource().extend({ get }).

Verification:

  • Zero loss: every diagnostic with all @ts-expect-error neutralized is identical before/after on TS 6, except the fixed Property 'current' does not exist.
  • Probed on TS 4.0, 4.1, 4.4, 4.9, 5.3 (legacy declarations) and TS 6/7.
  • Type perf (TS 6 instantiations): new 40-resource .extend('current').extend({ get }) scenario 153,915 → 156,556 (+1.7%, previously 40 errors); resources unchanged at 134,294; typical 12,995 → 13,398.

Open questions

From the original brief, two other gaps were checked:

  • ep.extend({ dataExpiryLength: 'long' }) already fails on master on every TS version from 4.0 to 7 (the message reads "not assignable to type 'never'"). This PR only adds regression tests for it.
  • getOptimisticResponse returning any is not fixed here. The right return type is the raw input the schema normalizes, which is what typed Controller.set() computes in core/src/controller/setManyTypes.ts. That file is being reworked for the set() updater perf regression, so sharing it is queued as a follow-up.

🤖 Generated with Claude Code

https://claude.ai/code/session_0117wFk9RgsnzgqUgJHDepyY


Generated by Claude Code


Note

Low Risk
Types-only change in @data-client/rest with no runtime behavior change; slightly richer intersection types (~1.7% more TS instantiations in one stress scenario).

Overview
Fixes a TypeScript-only gap when chaining resource().extend('name', …) with resource().extend({ get: … }) (or other standard endpoint overrides): runtime still had every endpoint, but the object-form return type was only CustomResource<…>, so names like current and deprecated create disappeared from the type.

The object overload of extend() now returns CustomResource<…> & Omit<R, keyof ResourceInterface | 'extend'> in resourceExtendable.ts and the TS 4.1 legacy declarations, so prior custom members stay on the inferred resource type through further chaining.

Adds typescript-tests/extendOptions.test.ts for member preservation and for extend() option validation regressions, plus a patch changeset and a v0.19 blog bullet.

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

resource().extend({ get: ... }) returned CustomResource, which only typed the
five standard endpoints, so members added earlier with extend('key', options)
(and the deprecated create) disappeared from the type while still present at
runtime.

Also add regression tests that extend() option types are checked
(dataExpiryLength etc.) across endpoint and resource extend forms.

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

changeset-bot Bot commented Oct 5, 2026 •

Copy link
Copy Markdown

🦋 Changeset detected

Latest commit: e1f1930

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

This PR includes changesets to release 4 packages
Name Type
@data-client/rest Patch
example-benchmark-react 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

@ntucker ntucker self-assigned this Oct 5, 2026
@vercel

vercel Bot commented Oct 5, 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 5, 2026 9:21am UTC

Request Review

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

github-actions Bot commented Oct 5, 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.64 kB
examples/test-bundlesize/dist/react.js 59.7 kB
examples/test-bundlesize/dist/webpack-runtime.js 784 B

compressed-size-action

@codecov

codecov Bot commented Oct 5, 2026

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 98.08%. Comparing base (74a964e) to head (e1f1930).
⚠️ Report is 17 commits behind head on master.

Additional details and impacted files
@@            Coverage Diff             @@
##           master    #4184      +/-   ##
==========================================
+ Coverage   98.06%   98.08%   +0.02%     
==========================================
  Files         163      165       +2     
  Lines        3098     3139      +41     
  Branches      617      625       +8     
==========================================
+ Hits         3038     3079      +41     
  Misses         18       18              
  Partials       42       42              

☔ 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 5, 2026 09:24

This branch was successfully deployed

1 active (outdated) deployment
Preview — 23b61c2e Deployed Oct 5, 2026 by vercel[bot]
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