Skip to content

program.getResolvedTypeReferenceDirectives() not keyed by containing file/directory, leading to invalid declaration emit #56836

Closed as not planned
@andrewbranch

Description

@andrewbranch

Test case

// @Filename: /node_modules/direct-dep/index.d.ts
/// <reference types="transitive-dep" />

// @Filename: /node_modules/direct-dep/node_modules/transitive-dep/index.d.ts
declare module "ambient-module" {
  export class C {
    private x;
  }
}

// @Filename: /index.ts
import { C } from "ambient-module";
export const c = new C();

// @Filename: /tsconfig.json
{
  "compilerOptions": {
    "declaration": true,
    "emitDeclarationOnly": true,
    "module": "nodenext",
    "types": ["direct-dep"]
  }
}

Expected behavior

Declaration files should be error-free if running tsc --declaration did not produce errors. Specifically, either /index.d.ts should contain a triple-slash reference to "direct-dep", or compiling should produce an error saying that a portable reference to /node_modules/direct-dep/node_modules/transitive-dep/index.d.ts cannot be emitted.

Actual behavior

/index.d.ts contains

/// <reference types="transitive-dep" />

which does not resolve. No errors are produced while compiling.

Discussion

The erroneous type reference directive is emitted because the program stores information that the target file, /node_modules/direct-dep/node_modules/transitive-dep/index.d.ts, had previously been resolved by a type reference directive of "transitive-dep" somewhere in the program’s input files. The declaration emitter mistakenly assumes that any type reference directive that resolved anywhere in the program will be resolvable from the declaration file it’s emitting, but this is a bad assumption because type reference resolution is dependent on the location of the file that’s making the resolution. In the test case above, the location that originally had the reference to "transitive-dep" has a node_modules folder in scope for its lookup that is not in scope for /index.d.ts.

This cannot reliably be fixed with a simple change to the declaration emitter, because the Program itself discards the relevant information about the location from which type reference directives were resolved. Its cache is keyed only by name and resolution mode.

I discovered this while attempting to make a package that uses conditional package.json "exports" to conditionally load ts-expose-internals. It fails because tsc writes invalid triple-slash references to "ts-expose-internals" instead of to my proxy package.

cc @sheetalkamat @weswigham for input

Metadata

Metadata

Assignees

No one assigned

    Labels

    BugA bug in TypeScript

    Type

    No type

    Projects

    No projects

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions