Skip to content

perf(server): make router inference types cheaper to type-check - #2454

Open
dinwwwh wants to merge 1 commit into
mainfrom
claude/perf-infer-router-types
Open

dinwwwh wants to merge 1 commit into
mainfrom
claude/perf-infer-router-types

Conversation

@dinwwwh

@dinwwwh dinwwwh commented Nov 13, 2026

Copy link
Copy Markdown
Member

Summary

This applies the same two changes as #2452 to the remaining types that walk a router with Lazyable<infer U>:

  • InferRouterInputs, InferRouterOutputs, InferRouterInitialContexts, InferRouterFinalContexts, InferRouterError and InferRouterErrors match a procedure through its ~orpc definition instead of the Procedure class.
  • Those six, and UnlaziedRouter, test each key against AnyRouter first, which has no infer. The Lazyable<infer U> inference now runs only for keys that are not plain routers.

Every type still recurses on itself; there is no new helper type.

Measurements

The base workload is the typecheck project of orpc-benchmarks: 500 routers, 3,000 procedures. Each row replaces its client calls with the lookups described. tsc --singleThreaded --extendedDiagnostics, TypeScript 7.0.2. "Before" is the published 2.0.0-beta.50, "after" is @orpc/server built from this branch. These counts are deterministic.

Workload Instantiations Types
InferRouterInputs and InferRouterOutputs, one lookup per procedure each 1,010,371 → 830,327 (−17.8%) 226,790 → 217,779 (−4.0%)
InferRouterInitialContexts, InferRouterFinalContexts and InferRouterErrors, one lookup per procedure each 953,314 → 726,737 (−23.8%) 192,260 → 183,245 (−4.7%)
Root router passed through unlazyRouter(), then read by a typed client call per procedure 1,168,031 → 1,112,523 (−4.8%) 253,867 → 250,863 (−1.2%)

InferRouterError has the same shape as InferRouterErrors and is changed the same way, but I did not build a separate workload for it.

This is independent of #2451, #2452 and #2453.

Behavior

No difference from main. I compared the old and new definitions of all seven types directly on procedures, nested routers, lazy routers and procedures, keys typed as Lazyable<...>, optional and readonly keys, os.router() output, AnyRouter, any and never: all resolve to identical types.

Emitted declarations are unchanged too. A consumer installed from packed tarballs (pnpm, depending only on @orpc/server) and built with composite: true, including generic code that takes a router as a type parameter, emits a .d.ts byte-identical to the one built against main, on TypeScript 7.0.2, 6.0.3 and 5.9.3.

Verification

pnpm check, pnpm test (4,585 tests) and pnpm docs:validate pass. The first pnpm test run had two failures in packages/bun (cancel and abort: bun-fetch), which are runtime timing tests this type-only change cannot reach; they passed in three separate re-runs and in a second full run.


Generated by Claude Code

InferRouterInputs, InferRouterOutputs, InferRouterInitialContexts,
InferRouterFinalContexts, InferRouterError and InferRouterErrors now
match a procedure through its `~orpc` definition instead of the
Procedure class. They, and UnlaziedRouter, also test each key against
AnyRouter before trying the `Lazyable<infer U>` inference, so only lazy
keys pay for it.

All of them resolve to the same types as before.

Co-Authored-By: Claude Fable 5.2 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01VVaw7SLCp5Vav859sAFS6N
@pkg-pr-new

pkg-pr-new Bot commented Nov 13, 2026

Copy link
Copy Markdown
More templates

@orpc/ai-sdk

npm i https://pkg.pr.new/@orpc/ai-sdk@2454

@orpc/arktype

npm i https://pkg.pr.new/@orpc/arktype@2454

@orpc/bun

npm i https://pkg.pr.new/@orpc/bun@2454

@orpc/client

npm i https://pkg.pr.new/@orpc/client@2454

@orpc/cloudflare

npm i https://pkg.pr.new/@orpc/cloudflare@2454

@orpc/contract

npm i https://pkg.pr.new/@orpc/contract@2454

@orpc/effect

npm i https://pkg.pr.new/@orpc/effect@2454

@orpc/evlog

npm i https://pkg.pr.new/@orpc/evlog@2454

@orpc/hibernation

npm i https://pkg.pr.new/@orpc/hibernation@2454

@orpc/json-schema

npm i https://pkg.pr.new/@orpc/json-schema@2454

@orpc/experimental-lock

npm i https://pkg.pr.new/@orpc/experimental-lock@2454

@orpc/mcp

npm i https://pkg.pr.new/@orpc/mcp@2454

@orpc/msw

npm i https://pkg.pr.new/@orpc/msw@2454

@orpc/nest

npm i https://pkg.pr.new/@orpc/nest@2454

@orpc/next

npm i https://pkg.pr.new/@orpc/next@2454

@orpc/node

npm i https://pkg.pr.new/@orpc/node@2454

@orpc/openapi

npm i https://pkg.pr.new/@orpc/openapi@2454

@orpc/opentelemetry

npm i https://pkg.pr.new/@orpc/opentelemetry@2454

@orpc/pinia-colada

npm i https://pkg.pr.new/@orpc/pinia-colada@2454

@orpc/pino

npm i https://pkg.pr.new/@orpc/pino@2454

@orpc/publisher

npm i https://pkg.pr.new/@orpc/publisher@2454

@orpc/ratelimit

npm i https://pkg.pr.new/@orpc/ratelimit@2454

@orpc/server

npm i https://pkg.pr.new/@orpc/server@2454

@orpc/shared

npm i https://pkg.pr.new/@orpc/shared@2454

@orpc/swr

npm i https://pkg.pr.new/@orpc/swr@2454

@orpc/tanstack-query

npm i https://pkg.pr.new/@orpc/tanstack-query@2454

@orpc/trpc

npm i https://pkg.pr.new/@orpc/trpc@2454

@orpc/valibot

npm i https://pkg.pr.new/@orpc/valibot@2454

@orpc/zod

npm i https://pkg.pr.new/@orpc/zod@2454

commit: 6b959bd

@coderabbitai

coderabbitai Bot commented Nov 13, 2026 •

Copy link
Copy Markdown

Explain Change →

No actionable comments were generated in the recent review.

Summary

Summary by CodeRabbit

  • TypeScript Improvements
    • Improved type inference for nested routers, including routers loaded lazily or referenced directly.
    • Router procedure types now provide more consistent inference for contexts, inputs, outputs, and errors.

Router inference types now extract procedure details from ~orpc metadata and recurse through direct router children as well as lazy routers. UnlaziedRouter now recursively maps properties that already extend AnyRouter.

Changes: Recursive Router Types

Layer / File(s) Summary
Procedure metadata and recursive inference
packages/server/src/router.ts
Router inference aliases extract contexts, schemas, and error maps from ~orpc procedure metadata. They now recurse through direct router children as well as lazy routers.
Recursive UnlaziedRouter mapping
packages/server/src/router-utils.ts
UnlaziedRouter recursively maps properties that extend AnyRouter, in addition to handling lazy router properties.

Estimated code review effort: 2 (Simple) | ~10 minutes

Change: Refactor

Possibly related PRs

  • middleapi/orpc#219: Adds UnlaziedRouter; this change extends that alias to map properties that already extend AnyRouter.
Merge Risk: Minimal · up to 6b959

This change makes router type inference cheaper to type-check without altering runtime behavior. No concrete issues were found, and the risk of merging is minimal.

Pre-merge checks: Passed 4

Check name Status Explanation
Title check Passed The title names the main change: router inference types are made cheaper to type-check, under a perf(server) scope. The objectives and summary describe the same change to the Infer Router* types and U…
Description check Passed The description states the purpose of the change. It lists the six Infer Router types and UnlaziedRouter that now match procedures through ~orpc and check AnyRouter before Lazyable inference. It also …
Linked Issues check Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check Passed Check skipped because no linked issues were found for this pull request.
Full details: Title check

Explanation

The title names the main change: router inference types are made cheaper to type-check, under a perf(server) scope. The objectives and summary describe the same change to the Infer Router* types and UnlaziedRouter, which matches the title's focus on type-check cost.

Full details: Description check

Explanation

The description states the purpose of the change. It lists the six Infer Router types and UnlaziedRouter that now match procedures through ~orpc and check AnyRouter before Lazyable inference. It also reports benchmark results, behavior checks, and verification runs, all of which relate to the changeset.

  • Autofix · Keep fixing CodeRabbit findings and required CI, and resolving merge conflicts

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

Share

Recent review info

Run configuration

  • Source mode: Auto
  • Repository: Unavailable
  • Branch: Unavailable
  • File: No configuration file from this repository applied
  • Commit: Unavailable
  • Layers: Repository UI
  • Review profile: CHILL
  • Plan: Advanced
  • Run ID: e0bbdf36-0a18-4c2b-ab2a-2db25e330190
Commits

Reviewing files that changed from the base of the PR and between 5c3b5c0 and 6b959bd.

Files selected for processing: 2

  • packages/server/src/router-utils.ts
  • packages/server/src/router.ts

Included review availability: This review used your included allowance. Your plan provides up to 8 included reviews per hour; 4 remain after this review.

@codspeed

codspeed Bot commented Nov 13, 2026

Copy link
Copy Markdown
Contributor

Merging this PR will improve performance by 62.11%

2 warnings

⚠️ 5 benchmarks are dominated by system calls

System calls cannot be simulated consistently and are left out of the measure. The reported value understates the real cost. Switch to the Walltime instrument to measure them.

Learn more about system calls

See affected benchmarks

⚠️ 1 benchmark with a significant change ran on different hardware

Its base and head results were measured on machines with a different CPU, core count or memory, so the change might not come from the code. Run base and head on the same runner type.

Learn more about Macro Runners

See affected benchmarks

⚡ 1 improved benchmark
✅ 27 untouched benchmarks
⏩ 2 skipped benchmarks1

Performance Changes

Benchmark BASE HEAD Efficiency
⚡ not modified (304) 1,078.5 µs 665.3 µs +62.11%

Tip

Curious why performance improved? Comment @codspeedbot explain why performance improved on this PR, or directly use the CodSpeed MCP with your agent.


Comparing claude/perf-infer-router-types (6b959bd) with main (5c3b5c0)

Open in CodSpeed

Footnotes

  1. 2 benchmarks were skipped, so the baseline results were used instead. If they were deleted from the codebase, click here and archive them to remove them from the performance reports. ↩

@codecov

codecov Bot commented Nov 13, 2026

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.

📢 Thoughts on this report? Let us know!

@pullfrog pullfrog 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.

✅ No new issues found.

Reviewed changes

  • Procedure detection via ~orpc — InferRouterInitialContexts, InferRouterFinalContexts, InferRouterInputs, InferRouterOutputs, InferRouterError and InferRouterErrors now match a procedure through ~orpc: ProcedureDefinition<...> instead of the Procedure class, so a decorated procedure no longer triggers a structural class comparison.
  • Router-first key check — those six types plus UnlaziedRouter test each key against AnyRouter before falling back to the Lazyable<infer U extends AnyRouter> inference, so the inferring union match only runs for lazy keys.

I compared the old and new definitions directly with a throwaway type probe (bare procedure, nested router, Lazy<procedure>/Lazy<router> keys, Lazyable and union keys, optional and readonly keys, AnyRouter, AugmentedRouter output, any, never) and every one of the seven types resolved identically. The branch order is sound: Lazy<T> is not assignable to AnyRouter or to the ~orpc definition shape, so the fast path cannot shadow lazy unwrapping. Root tsc -p tsconfig.json --noEmit is clean.

Pullfrog  | View workflow run | Using deepseek-v4.1-flash-1017 (free via Pullfrog for OSS) | 𝕏

@cloudflare-workers-and-pages

Copy link
Copy Markdown

Deploying with  Cloudflare Workers  Cloudflare Workers

The latest updates on your project. Learn more about integrating Git with Workers.

Status Name Latest Commit Preview URL Updated (UTC)
✅ Deployment successful!
View logs
orpc 6b959bd Commit Preview URL

Branch Preview URL
Nov 13 2026, 12:41 AM

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

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants