Speed up codebase type-checking by collapsing dispatcher path overloads#800
Conversation
The setCollectionDispatcher() and setOrderedCollectionDispatcher() overloads typed `path` as RFC 6570 template-literal unions: `Rfc6570Expression` is an 8-member union, and the overloads interpolated it up to four times, so each `path` type expanded to a Cartesian product of up to 8^4 = 4096 template-literal variants. TypeScript spent almost all of `deno check`'s time structurally comparing these types, making a cold check of middleware.ts take on the order of ~30s. Collapse those overloads to a single `path: string` signature in both the Federatable interface and FederationBuilderImpl. Path validity is still enforced at runtime via @fedify/uri-template (isPath() and Router.variables(), already used in builder.ts), so nothing is lost except the costly compile-time RFC 6570 shape checking. fedify-dev#613 Assisted-by: Claude Code:claude-opus-4-8 Assisted-by: Codex:gpt-5.5
Same root cause as the previous commit: setObjectDispatcher() still typed `path` as RFC 6570 template-literal unions. Its widest overloads interpolated `Rfc6570Expression` (an 8-member union) up to three times, expanding `path` to 8^3 = 512 template-literal variants, plus several simple multi-variable forms. These were the last remaining combinatorial dispatcher path types, and the context boundary checks (ContextImpl/FederationImpl vs Context/Federation) dragged them in throughout the type graph. Collapse the setObjectDispatcher() overloads to a single `path: string` signature in both the Federatable interface and FederationBuilderImpl. Path validity (including the "one or more variables" requirement) is still enforced at runtime via assertPath() and Router.variables() in the existing implementation. This roughly halves the remaining cold project type-check: locally `deno task check:types` drops from ~24s to ~12s, on top of the ~99s -> ~24s from the previous commit. fedify-dev#613 Assisted-by: Claude Code:claude-opus-4-8 Assisted-by: Codex:gpt-5.5
|
No actionable comments were generated in the recent review. 🎉 ℹ️ Recent review info⚙️ Run configurationConfiguration used: Repository UI Review profile: ASSERTIVE Plan: Pro Run ID: 📒 Files selected for processing (2)
📝 WalkthroughWalkthroughThe PR consolidates three public dispatcher registration method signatures in ChangesDispatcher method signature consolidation
Estimated code review effort🎯 2 (Simple) | ⏱️ ~8 minutes 🚥 Pre-merge checks | ✅ 5✅ Passed checks (5 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches🧪 Generate unit tests (beta)
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. Comment |
There was a problem hiding this comment.
Code Review
This pull request simplifies the method signatures of setObjectDispatcher, setCollectionDispatcher, and setOrderedCollectionDispatcher in FederationBuilderImpl by removing several complex template literal type overloads for the path parameter. I have no feedback to provide as there are no review comments.
Important
The consumer version of Gemini Code Assist on GitHub is being sunset. Starting June 18, 2026, new organization installations will be blocked, and all code review activity will officially cease on July 17, 2026.
For more details on the timeline and next steps, please review the Help Documentation.
dahlia
left a comment
There was a problem hiding this comment.
Although it's not a hard change, but since speed-ups are good news, we should add an entry about this speed-up to CHANGES.md.
Record in CHANGES.md that TypeScript type-checking was sped up by simplifying the internal `path` parameter types of the object, collection, and ordered collection dispatcher methods, dropping a full codebase type check from ~99s to ~13s. fedify-dev#613 fedify-dev#800 Assisted-by: Claude Code:claude-opus-4-8
Addressed in 878ab20. |
Codecov Report✅ All modified and coverable lines are covered by tests.
... and 17 files with indirect coverage changes 🚀 New features to boost your workflow:
|
sij411
left a comment
There was a problem hiding this comment.
I can reproduce that this meaningfully improves the expensive type-check phase, so I agree this is relevant to #613.
In my local A/B check against the PR base, the workspace deno check phase got much faster after this change. So this does address one real source of the slowdown.
That said, I would be careful about saying this fully fixes #613. The issue asks for the main slowdown sources to be understood and documented, and for check-all/pre-commit validation to become meaningfully faster without reducing coverage. This PR identifies and improves one major source, but it does not by itself show that the remaining check-all cost is understood, nor does it include a reusable benchmark/diagnostic artifact that future changes can compare against.
There is also a subtle API-boundary point worth documenting. Calls through the supported factory path still preserve the public overloads:
createFederationBuilder<void>().setObjectDispatcher(
Note,
"/notes/{id}",
(_ctx, values) => {
values.id satisfies string;
// @ts-expect-error still rejected
values.missing;
return null;
},
);But direct use of the implementation class no longer has that compile-time protection:
new FederationBuilderImpl<void>().setObjectDispatcher(
Note,
"/notes/{id}",
(_ctx, values) => {
// @ts-expect-error now unused
values.missing;
return null;
},
);This seems acceptable if FederationBuilderImpl is intentionally internal; it is not re-exported from @fedify/fedify or @fedify/fedify/federation. But I think the PR description should say that explicitly: the public FederationBuilder/Federatable surface is unchanged, while direct source-level use of FederationBuilderImpl moves those checks from compile time to runtime.
So I would suggest either:
- clarify that this is a partial fix for #613 focused on the dispatcher overload hot path; and
- add a small type-level regression test for the supported factory/public path, or document
FederationBuilderImplas internal.
Document on setObjectDispatcher(), setCollectionDispatcher(), and setOrderedCollectionDispatcher() that the RFC 6570 template-literal `path` overloads were removed for type-checking efficiency, so URI variable types can no longer be inferred from `path` and the variable name should be supplied through the `TParam` generic argument instead. Also clarify in CHANGES.md that this is a partial fix for the issue below, targeting the dispatcher overload hot path; other contributors to check-all cost may remain. fedify-dev#613 fedify-dev#800 Assisted-by: Claude Code:claude-opus-4-8
Document on setObjectDispatcher(), setCollectionDispatcher(), and setOrderedCollectionDispatcher() that the RFC 6570 template-literal `path` overloads were removed for type-checking efficiency, so URI variable types can no longer be inferred from `path` and the variable name should be supplied through the `TParam` generic argument instead. Also clarify in CHANGES.md that this is a partial fix for the issue below, targeting the dispatcher overload hot path; other contributors to check-all cost may remain. Requested by @sij411 in fedify-dev#800 (review) fedify-dev#613 fedify-dev#800 Assisted-by: Claude Code:claude-opus-4-8
ee801b8 to
bb2ef0a
Compare
Addressed in bb2ef0a. Thanks! |
Fixes #613.
Background
The$8^4 = 4096$ template-literal variants.
setObjectDispatcher(),setCollectionDispatcher(), andsetOrderedCollectionDispatcher()methods onFederationBuilderImpltyped theirpathparameter as RFC 6570 template-literal unions.Rfc6570Expression<TParam>is an 8-member union, and these overloads interpolated it up to four times, so eachpathtype expanded to a Cartesian product of up toTypeScript spent almost all of
deno check's time structurally comparing these enormous unions. The cost was not confined to a single file: the context boundary checks (ContextImpl/FederationImplvsContext/Federation) dragged these path types in throughout the type graph, so the combinatorial blowup propagated across the whole project.Change
Collapse the affected dispatcher overloads on
FederationBuilderImplto a singlepath: stringsignature:setObjectDispatcher()— removed 6 template-literal overloads (interpolatedRfc6570Expressionup to three times, 8^3 = 512 variants, plus several simple multi-variable forms).setCollectionDispatcher()andsetOrderedCollectionDispatcher()— removed the template-literal overloads (up to 8^4 = 4096 variants each).The net diff is 183 deletions in a single file (
packages/fedify/src/federation/builder.ts).Public API is unchanged
The template-literal overloads are kept on the public
Federatableinterface (and thus onFederationandFederationBuilder, which extend it). Users still get the same compile-time path validation and autocompletion. Only the internal implementation class signature was collapsed; becausepath: stringis a wider, compatible implementation signature, it still satisfies the interface's narrower overloads.Nothing is lost at runtime
Path validity—including the "one or more variables" requirement—is still enforced at runtime via
assertPath()andRouter.variables()(backed by@fedify/uri-template), exactly as before. The only thing removed is the costly compile-time RFC 6570 shape checking.Impact
Full-codebase type-checking (
mise run check:types) drops from ~99s to ~13s.