Repository: TypeScript
Issue: Different behavior for type reference directive in nodenext based on whether types are in @types or not
Labels: Needs Investigation

# Bug Report

<!--
  Please fill in each section completely. Thank you!
-->

### 🔎 Search Terms

type reference directive node12 nodenext

<!--
  What search terms did you use when trying to find an existing bug report?
  List them here so people in the future can find this one more easily.
-->

### 💻 Fourslash server test case

I used fourslash server tests to verify this because it treats files in node_modules as a real tsc run would, whereas the compiler test suite treats them as if they’re root files for compilation, which messes up what I’m trying to demonstrate.

I have two dependencies in node_modules here that are structurally identical, but one is inside `@types` and the other is not. The one in `@types` is resolved by the `primaryLookup` of `resolveTypeReferenceDirective` in moduleNameResolver.ts, which

- only looks in `typeRoots` (including `@types`)
- only accepts `.ts` and `.d.ts` files (not the cts/mts variants)
- does not look at `exports`

The one outside of `@types`, by merit of not being in `typeRoots`, uses the `secondaryLookup` function, which uses a very different algorithm that brings in some Node12/NodeNext resolution features. It

- looks in `node_modules/*` first before trying `node_modules/@types/*`
- accepts cts/mts file extensions
- looks at `exports` with conditions `node, require, types`

This test case shows how this discrepancy can make a difference in resolution based only on whether the package is inside `typeRoots` or not.

I don’t know what the expected behavior _is_ here, but it feels like it should probably be the same whether the package is in `@types` or regular `node_modules`.

```ts
// @Filename: /tsconfig.json
//// {
////     "compilerOptions": {
////         "module": "nodenext",
////         "types": ["inside-at-types", "outside-at-types"]
////     }
//// }

// @Filename: /node_modules/@types/inside-at-types/package.json
//// {
////   "name": "@types/inside-at-types",
////   "version": "1.0.0",
////   "types": "./index.d.ts",
////   "exports": {
////     ".": {
////       "default": "./main.mjs"
////     }
////   }
//// }

// @Filename: /node_modules/@types/inside-at-types/index.d.ts
//// export {};
//// declare global {
////   var typesFieldInsideAtTypes: any;
//// }

// @Filename: /node_modules/@types/inside-at-types/main.d.mts
//// export {};
//// declare global {
////   var exportsInsideAtTypes: any;
//// }

// @Filename: /node_modules/outside-at-types/package.json
//// {
////   "name": "outside-at-types",
////   "version": "1.0.0",
////   "types": "./index.d.ts",
////   "exports": {
////     ".": {
////       "default": "./main.mjs"
////     }
////   }
//// }

// @Filename: /node_modules/outside-at-types/index.d.ts
//// export {};
//// declare global {
////   var typesFieldOutsideAtTypes: any;
//// }

// @Filename: /node_modules/outside-at-types/main.d.mts
//// export {};
//// declare global {
////   var exportsOutsideAtTypes: any;
//// }

// @Filename: /index.ts
//// // One pair of these should be resolved;
//// // the other unresolved. Currently, they're mixed.
////
//// typesFieldInsideAtTypes;
//// typesFieldOutsideAtTypes;

//// exportsInsideAtTypes;
//// exportsOutsideAtTypes;

goTo.file("/index.ts");
verify.baselineSyntacticAndSemanticDiagnostics();
```

### 🙁 Actual behavior

`exportsInsideAtTypes` and `typesFieldOutsideAtTypes` are unresolved (meaning inside `@types` resolves to the `types` field while outside `@types` resolves to the `exports` field)

### 🙂 Expected behavior

Either both `typesFieldInsideAtTypes` and `typesFieldOutsideAtTypes` or both `exportsInsideAtTypes` and `exportsOutsideAtTypes` should be unresolved, while the other pair is resolved.

(@DanielRosenwasser @weswigham)