Repository navigation
Description
Versions
- Fails:
[email protected](tsgo) - Works:
[email protected]on the identical program and dependency tree - Module resolution:
bundler;strict,verbatimModuleSyntaxon
Context
Real-world case from a TanStack Start 1.168 app:
@tanstack/router-coredeclares the interface:// @tanstack/router-core/dist/esm/router.d.ts export interface Register { /* ... */ }
@tanstack/react-routeronly re-exports it:// @tanstack/react-router/dist/esm/index.d.ts export type { Register /* ... */ } from '@tanstack/router-core';
- App code augments the re-exporting module name:
// src/server.ts declare module "@tanstack/react-router" { interface Register { server: { requestContext: { env: Env; ctx: Ctx } } } }
- A third package's declaration imports the re-export and feeds it through a conditional type:
// @tanstack/react-start .../server-entry.d.ts import { Register } from '@tanstack/react-router'; export type ServerEntry = { fetch: RequestHandler<Register> }; // start-server-core request-handler.d.ts export type RequestOptions<TRegister> = EarlyHintsOptions & InlineCssOptions & (TRegister extends { server: { requestContext: infer TRequestContext } } ? { context: TRequestContext & BaseContext } // augmented branch : { context?: BaseContext }); // unaugmented fallback
Actual behavior (7.0.2)
Whether the conditional picks the augmented or unaugmented branch depends on how the same root files enter the program:
| Program configuration | Result under 7.0.2 | Under 6.0.3 |
|---|---|---|
"include": ["src"] (+ exclude patterns), ~26 .ts/.tsx roots |
❌ unaugmented branch — excess-property error at the handler call site | ✅ augmented |
byte-identical root set enumerated as an explicit include array of file paths |
✅ augmented | ✅ augmented |
| single-file programs / small synthetic programs using the same packages | ✅ augmented | ✅ augmented |
Each row is deterministic across repeated runs. Probing RequestOptions<Register> with a discriminating assignment confirms TS 6 reduces it to { context: TRequestContext & BaseContext } while TS 7 reduces it to { context?: BaseContext } in the failing configuration.
Notably, a second augmentation of the declaring module (declare module "@tanstack/router-core") does land somewhere — conflicting duplicate declarations across files produce TS2717 "Subsequent property declarations must have the same type", naming the first declaration's shape — yet the conditional still resolves to the unaugmented branch, suggesting two distinct Register symbols end up existing per program state.
Minimization attempts
A synthetic two-package repro (local @fake/core declaring the interface + @fake/reexport doing export type { Register }, one augmenting file + one consumer probing the conditional) compiles fine under 7.0.2 even with 150 additional modules mixed into the glob. The flip appears to require the scale/structure of the real program (route-tree codegen that also augments Register.router, dozens of cross-importing route modules, etc.).
Workaround we shipped
Typing the single affected call site explicitly against our own shape and keeping the augmentation for documentation value:
const fetchHandler = handler.fetch as unknown as (
request: Request,
opts: { context: QuesonaWebRequestContext }
) => Promise<Response>Happy to provide the full failing program snapshot or run diagnostics (--explainFiles output comparing both configurations shows an identical resolved file set for the router packages) if useful.
Activity
RyanCavanaugh commentedon Aug 25, 2026
I couldn't reproduce this from the information in the issue.
I tested a synthetic three-package setup matching the described topology:
@fake/coredeclaresRegister@fake/reexporttype-only re-exportsRegister@fake/startimports that re-export and evaluates the described conditional typesrc/augmentation.tsaugments@fake/reexportsrc/consumer.tsassigns{ context: { env: "production" } }to the resulting typeI compiled the same two root files both through
"include": ["src"]and through the explicit list"include": ["src/augmentation.ts", "src/consumer.ts"]. Both configurations used:{ "module": "preserve", "moduleResolution": "bundler", "strict": true, "verbatimModuleSyntax": true, "noEmit": true }All four compilations completed without diagnostics:
[email protected][email protected][email protected][email protected]Since the issue notes that this synthetic form does not reproduce the failure and the full failing program snapshot is not attached, there isn't currently a reproducible test case to investigate or bisect. Please provide the full snapshot together with both tsconfig variants (with any private application code reduced or removed); it appears that some additional part of the real program is necessary to trigger the behavior.
feat(dsh-idle-archive): i18n 试点——双语字典 + 官方 locale 双通道接入(#348)
feat: 0.1.2-alpha.2 适配 + 六包 i18n 接入(#323 #348)
chore(dsh-provider-usage): 过期文档以当前架构为准清理 + 源码注释去过程标记(#681) (#686)
unrevised6419 commentedon Sep 23, 2026
In a way I think this is expected, because the module augmentation of interface
Registerin module@tanstack/react-routeris not actually augmenting it, but creates a newRegisterinterface in that module (probably this needs to be documented if not yet already), that completely overrides the one from@tanstack/router-core.Was encountering similar problems with Strapi packages.
I made a YT video, and a reproduction repo with details if this can help.
new(better explained): https://youtu.be/l9UE2DNZ4vc
new(unreliable workaround): https://youtu.be/hkqEx68ZdqQ
old: https://youtu.be/KvYVjRjzCp8
old: https://youtu.be/8hwu8TeUx6Q
Repo: https://github.com/unrevised6419/_test-ts-namespace-augment
Related: strapi/strapi#27747