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

Labels

Needs InvestigationThis issue needs a team member to investigate its status.

Type

No type

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions