Search Terms
declaration emit, JSDoc, @import, private import, wrong module specifier, TS2694, symlink
Version & Regression Information
- Reproduces with TypeScript 7.0.2.
- Reproduces on the current native compiler main branch at
5b1047d10d32e7d5b446be4de56b126ff42f82bb.
- Does not reproduce with TypeScript 6.0.3.
- This is therefore a regression between TypeScript 6.0.3 and 7.0.2.
Reproduction
https://github.com/platypii/typescript-7-jsdoc-private-alias-repro
npm ci
npm run build # succeeds
npm run check # fails with TS2694
There is no Playground link because the reproduction depends on declaration emit across multiple modules and a physically installed dependency.
Code
/**
* @import {SchemaElement} from 'external'
* @import {Writer} from '../src/types.js'
*/
export class Example {
/**
* @param {object} options
* @param {SchemaElement[]} options.schema
*/
constructor({ schema }) {
this.schema = schema
}
}
src/types.d.ts imports the same external type privately:
import type { SchemaElement } from 'external'
export interface Writer {}
The JavaScript is compiled with allowJs, checkJs, declaration, and emitDeclarationOnly enabled.
Actual behavior
TypeScript 7 emits:
export declare class Example {
schema: import('../src/types.js').SchemaElement[]
}
The generated declaration is invalid because src/types.d.ts does not export SchemaElement. A consumer reports:
TS2694: Namespace '.../src/types' has no exported member 'SchemaElement'.
The Writer import is otherwise unused. Removing only that JSDoc import causes TypeScript 7 to emit a valid reference to the external package instead.
Expected behavior
Declaration emit should reference the public source of the type, for example:
schema: import('external/src/types.js').SchemaElement[]
At minimum, declaration emit should not select an alias that is not exported from the referenced module.
Additional investigation
I traced the 7.0.2 compiler and found that the node builder selects the correct symbol from node_modules/external; the incorrect path is introduced later while generating the module specifier.
For a physically installed package, ResolvePackageDirectory returns an empty OriginalPath. GetSymlinkCache then combines that empty value with package.json, and ProcessResolution resolves the resulting relative path against the project directory. This records a false symlink mapping from the project root to node_modules/external. The module specifier generator consequently treats the project's src/types.d.ts as an alternative source file and reuses the unrelated JSDoc module specifier.
Guarding that path with packageResolution.OriginalPath != "" prevents the false mapping and produces the valid declaration in this reproduction.
The reproduction's .npmrc only makes its local file: fixture install as a physical directory, matching an ordinary registry-installed dependency. Users do not need that setting to encounter the bug in a real project; the README explains this in more detail.
Search Terms
declaration emit, JSDoc,
@import, private import, wrong module specifier, TS2694, symlinkVersion & Regression Information
5b1047d10d32e7d5b446be4de56b126ff42f82bb.Reproduction
https://github.com/platypii/typescript-7-jsdoc-private-alias-repro
There is no Playground link because the reproduction depends on declaration emit across multiple modules and a physically installed dependency.
Code
src/types.d.tsimports the same external type privately:The JavaScript is compiled with
allowJs,checkJs,declaration, andemitDeclarationOnlyenabled.Actual behavior
TypeScript 7 emits:
The generated declaration is invalid because
src/types.d.tsdoes not exportSchemaElement. A consumer reports:The
Writerimport is otherwise unused. Removing only that JSDoc import causes TypeScript 7 to emit a valid reference to the external package instead.Expected behavior
Declaration emit should reference the public source of the type, for example:
At minimum, declaration emit should not select an alias that is not exported from the referenced module.
Additional investigation
I traced the 7.0.2 compiler and found that the node builder selects the correct symbol from
node_modules/external; the incorrect path is introduced later while generating the module specifier.For a physically installed package,
ResolvePackageDirectoryreturns an emptyOriginalPath.GetSymlinkCachethen combines that empty value withpackage.json, andProcessResolutionresolves the resulting relative path against the project directory. This records a false symlink mapping from the project root tonode_modules/external. The module specifier generator consequently treats the project'ssrc/types.d.tsas an alternative source file and reuses the unrelated JSDoc module specifier.Guarding that path with
packageResolution.OriginalPath != ""prevents the false mapping and produces the valid declaration in this reproduction.The reproduction's
.npmrconly makes its localfile:fixture install as a physical directory, matching an ordinary registry-installed dependency. Users do not need that setting to encounter the bug in a real project; the README explains this in more detail.