Versions
- Fails:
typescript@7.0.2 (tsgo)
- Works:
typescript@6.0.3 on the identical program and dependency tree
- Module resolution:
bundler; strict, verbatimModuleSyntax on
Context
Real-world case from a TanStack Start 1.168 app:
@tanstack/router-core declares the interface:
// @tanstack/router-core/dist/esm/router.d.ts
export interface Register { /* ... */ }
@tanstack/react-router only 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.
Versions
typescript@7.0.2(tsgo)typescript@6.0.3on the identical program and dependency treebundler;strict,verbatimModuleSyntaxonContext
Real-world case from a TanStack Start 1.168 app:
@tanstack/router-coredeclares the interface:@tanstack/react-routeronly re-exports it:Actual behavior (7.0.2)
Whether the conditional picks the augmented or unaugmented branch depends on how the same root files enter the program:
"include": ["src"](+ exclude patterns), ~26 .ts/.tsx rootsincludearray of file pathsEach 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 distinctRegistersymbols end up existing per program state.Minimization attempts
A synthetic two-package repro (local
@fake/coredeclaring the interface +@fake/reexportdoingexport 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 augmentsRegister.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:
Happy to provide the full failing program snapshot or run diagnostics (
--explainFilesoutput comparing both configurations shows an identical resolved file set for the router packages) if useful.