microsoft / microsoft/TypeScript

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

Open
#62,806 3 comments 0 reactions 1 assignee Claimed by @weswigham View on GitHub
Needs Investigation
Dominant language
Go
Stars
111k
Forks
14.3k
Avg merge
2d 4h
Merged PRs (30d)
132

Description

### 🔎 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](https://www.typescriptlang.org/docs/handbook/release-notes/typescript-4-7.html#packagejson-exports-imports-and-self-referencing). 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`.

```mermaid
graph LR
a --> b
a --> c
b --> c
```

```ts
// packages/a/src/index.ts

import { B } from "b";

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

export const c = b.c;
```

```ts
// packages/b/src/index.ts

import { C } from "c";

export interface B {
readonly c: C;
}
```

```ts
// packages/c/src/index.ts

export type { C } from "./C";
export type { C2 as C2 } from "./C2";
```

```ts
// packages/c/src/C.ts

export interface C {
readonly foo: "bar";
}
```

```ts
// 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.

```console
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`.

```console
# 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_

Contributor guide

Open the contributing guide

Assessment

This issue has not been assessed yet.

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.