Skip to content

export const x = expr as Generic<A, B> produces a phantom export named after B, colliding when 2+ such casts share a last type arg #296

Closed

Description

@tmair

import-in-the-middle's ESM export scanner (lib/get-esm-exports.mjs → es-module-lexer) misparses a specific TypeScript cast shape: export const x = expr as GenericType<Arg1, Arg2>;. When the cast target has 2+ comma-separated type arguments, the scanner emits a phantom second export named after the literal text of the last type argument — not tied to any real binding.

Not specific to the word unknown — swapping it for number produces a phantom number export instead. But unknown is exactly what codegen tools emit as the trailing type arg for typed documents with no variables (e.g. graphql-code-generator's typed-document-node plugin: export const MyQueryDocument = {...} as unknown as DocumentNode<MyQuery, unknown>;). Any generated file with 2+ such exports gets two phantom "unknown" exports, which collide in the generated wrapper module — invalid ESM, crashes the module linker before user code runs:

SyntaxError: Duplicate export of 'unknown'

Repro

Full runnable repro: https://gist.github.com/tmair/a10f56297ca85729faabedc897d47918

git clone https://gist.github.com/a10f56297ca85729faabedc897d47918.git phantom-export

Run:

npm install
node --experimental-loader=import-in-the-middle/hook.mjs --import ./register.mjs app.mjs

Expected: 1

Actual:

file:///.../types.ts?iitm=true:16
export { $0 as "foo", $1 as "unknown", $2 as "bar", $3 as "unknown" }
                                                    ^^^^^^^^^^^^^^^
SyntaxError: Duplicate export of 'unknown'

Environment

  • import-in-the-middle: reproduces on 3.5.0 and 3.5.1 (latest)
  • Node.js: v26.3.0 locally; also seen on v24.21.0 in production
  • Triggered via Node's native TypeScript execution (running .ts files directly, no separate transpile step) combined with --experimental-loader=import-in-the-middle/hook.mjs

Boundary conditions checked

  • Single-arg generic cast (1 as Array<unknown>) — no phantom export.
  • Type annotation instead of cast (const foo: Generic<A, B> = ...) — no phantom export.
  • Only a cast expression as Generic<Arg1, Arg2> with 2+ type args triggers it; phantom export name = last type argument's text.

Activity

  1. tayfuryldz commented on Sep 18, 2026

    @tayfuryldz

    I can take this. I’ll reproduce it against current main through the native TypeScript loader path, add a regression around multiple generic-cast exports, and keep the fix scoped to TypeScript export discovery.

  2. BridgeAR commented on Oct 5, 2026

    @BridgeAR
    Member

    Please update your dependencies and verify the behavior with the latest es-module-lexer version being just released. It would be pulled in automatically

  3. BridgeAR commented on Oct 15, 2026

    @BridgeAR
    Member

    Closing as resolved with the latest es-module-lexer version

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions