Skip to content

Self-referencing causes non-portable inferred types (TS2742) false positive #62806

@gluxon

Description

@gluxon

🔎 Search Terms

  • The inferred type of '...' cannot be named without a reference to '...'. This is likely not portable. A type annotation is necessary.
  • TS2742
  • Project reference redirect
  • Self-reference
  • Build mode
  • Watcher

🕗 Version & Regression Information

Versions 4.7 - 5.9.3.

I've reproduced this in TypeScript 4.7.2, which is when self-referencing was first announced as a feature. This bug appears on the latest version of TypeScript at the time of writing (i.e. 5.9.3).

⏯ Playground Link

This issue requires multiple NPM packages. Small repro at https://github.com/gluxon/typescript-portability-error-false-positive-due-to-self-import

💻 Code

I've set up a repro at https://github.com/gluxon/typescript-portability-error-false-positive-due-to-self-import.

The code below is from the repro above and should match exactly. The TypeScript for the repro itself is only ~16 lines of code. In these packages, package a depends on b + c, and package b depends on c.

graph LR
  a --> b
  a --> c
  b --> c
Loading
// packages/a/src/index.ts

import { B } from "b";

const b: B = {
  c: { foo: "bar" },
};

export const c = b.c;
// packages/b/src/index.ts

import { C } from "c";

export interface B {
  readonly c: C;
}
// packages/c/src/index.ts

export type { C } from "./C";
export type { C2 as C2 } from "./C2";
// packages/c/src/C.ts

export interface C {
  readonly foo: "bar";
}
// packages/c/src/C2.ts

// 🚨 This self-reference causes the non-portable type false positive. 🚨
// Importing from "./C" instead of "c" fixes the issue.
import { C } from "c";

export type C2 = C;

🙁 Actual behavior

When running tsc with build mode, a false positive error is shown.

tsc --build --verbose packages/a/tsconfig.json
[8:40:22 PM] Building project '/Volumes/git/typescript-false-positive-non-portable-watcher-error/packages/a/tsconfig.json'...

packages/a/src/index.ts:7:14 - error TS2742: The inferred type of 'c' cannot be named without a reference to '../node_modules/c/src'. This is likely not portable. A type annotation is necessary.

7 export const c = b.c;
               ~

The error above should not happen since:

  1. Running tsc without the build flag does not show this issue. (See "Expected Behavior" below.)
  2. The portability error is non-sensical. It's looking for a reference to package c's source code in ../node_modules/c/src rather than its built .d.ts files. I've narrowed this to a bug with project reference redirects.
  3. The portability error happens even when c is a dependency of a.

🙂 Expected behavior

Running tsc without the --build flag results in a successful compilation of package a.

# Compile manually in topological order.
tsc -p packages/c/tsconfig.json
tsc -p packages/b/tsconfig.json
tsc -p packages/a/tsconfig.json

Additional information about the issue

No response

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