TypeScript icon indicating copy to clipboard operation
TypeScript copied to clipboard

TS2742 “The inferred type of 'default' cannot be named” with ESLint `defineConfig`

Open andersk opened this issue 2 months ago • 10 comments

🔎 Search Terms

TS2742, 2742, composite, pnpm

🕗 Version & Regression Information

  • This is the behavior in every version I tried, and I reviewed the FAQ for entries about TS2742

⏯ Playground Link

No response

💻 Code

eslint.config.ts

import { defineConfig } from "@eslint/config-helpers";
export default defineConfig();

node_modules/@eslint/config-helpers/index.d.ts

export type Config = import("@eslint/core").ConfigObject;
export function defineConfig(): Config;

node_modules/@eslint/config-helpers/node_modules/@eslint/core/index.d.ts

export interface ConfigObject {}

tsconfig.json

{
  "compilerOptions": {
    "composite": true
  }
}

(No other files are needed for this test case; there’s nothing else in node_modules.)

🙁 Actual behavior

No error.

🙂 Expected behavior

eslint.config.ts(2,1): error TS2742: The inferred type of 'default' cannot be named without a reference to '@eslint/config-helpers/node_modules/@eslint/core'. This is likely not portable. A type annotation is necessary.

Additional information about the issue

In eslint/rewrite#283 I reported an unexpected error TS2742 when using the defineConfig function from ESLint. I reduced this test case to a clear TypeScript issue, which I’m reporting as requested in https://github.com/microsoft/TypeScript/issues/42873#issuecomment-2065572017.

This change makes the error disappear:

--- a/node_modules/eslint/node_modules/@eslint/config-helpers/index.d.ts
+++ b/node_modules/eslint/node_modules/@eslint/config-helpers/index.d.ts
@@ -1,2 +1,3 @@
-export type Config = import("@eslint/core").ConfigObject;
+import { ConfigObject as Config } from "@eslint/core";
+export { Config };
 export function defineConfig(): Config;

However, in the real @eslint/config-helpers package, the line export type Config = import("@eslint/core").ConfigObject is generated by tsc from JSDoc /** @typedef {import("@eslint/core").ConfigObject} Config */, and there doesn’t seem to be a way to generate an equivalent change from JSDoc.

In any case, such a change shouldn’t be necessary. TypeScript ought to recognize that export type Config = import("@eslint/core").ConfigObject is already sufficient to allow the defineConfig return type to be named.

andersk avatar Oct 07 '25 08:10 andersk

(this response was auto-generated)

FAQ Responses

The inferred type of "X" cannot be named without a reference to "Y". This is likely not portable. A type annotation is necessary

The issue described pertains to the TypeScript error TS2742, which arises when TypeScript cannot determine a portable way to reference a type due to dependency structures in your project. In your eslint.config.ts, TypeScript faces a similar challenge because the type Config inferred from defineConfig() is tied to deep, indirect imports within your node_modules.

The TypeScript FAQ explains that when TypeScript emits declaration files, it must determine the correct module path for types. If paths involve indirect, nested node_modules that are not directly declared as dependencies in your package.json, TypeScript might raise a TS2742 error. This is because it cannot guarantee these paths will be valid in the consuming environment.

To resolve this, the FAQ suggests ensuring that any types you rely upon are directly imported or declared in a way that TypeScript can consistently name and reference them. Since TypeScript 5.5 offers improvements to handle these situations, ensuring that these deep module paths have correlating entries in your package.json dependencies might reduce these issues.


Similar Issues

Here are the most similar issues I found:

  • #54878 - TS4082 occurs when using composite: true (64% similar)
    • This issue: No summary available
    • Your issue: The issue reported involves a TS4082 error occurring in TypeScript when the composite option is enabled within a tsconfig.json file. This problem arises specifically with the export default syntax in a code block that involves an inline interface definition. When composite is set to true, this syntax triggers a compiler error, suggesting a misalignment in how TypeScript handles inline interfaces compared to other constructs like type aliases within composite projects. The expectation is for consistent behavior, either having both instances of the code throw an error or neither, regardless of the composite setting.
  • #24033 - JSDoc not recognized for default export expressions (63% similar)
    • This issue: No summary available
    • Your issue: The issue reported concerns the lack of JSDoc type recognition for default export expressions in JavaScript code when using TypeScript version 2.8.3. While JSDoc correctly infers types for variables declared with const, issues arise when using JSDoc comments for default export expressions. Despite annotations like /** @type {webpack.Configuration[]} */ and /** @export {webpack.Configuration[]} */ in the code, the expected type annotation is not recognized by TypeScript for the default exported array, leading to incorrect type inference.
  • #33027 - Using ES6 def. values for React component gives type error (62% similar)
    • This issue: No summary available
    • Your issue: The issue reported involves a type error encountered when trying to use ES6 default values in a React functional component with TypeScript version 3.7.0-dev.20190822. The component is defined with default values for its properties, yet TypeScript indicates a missing property error (TS2741) when the component is instantiated without passing all required properties. This error occurs because the component's interface strictly defines the properties as required, despite the default values specified within the component's implementation, conflicting with TypeScript's type checks for required properties.
  • #36856 - allowSyntheticDefaultImports doesn't work when mixing commonJS and ES6 modules (62% similar)
    • This issue: No summary available
    • Your issue: The TypeScript version in use for this issue is 3.9.0-dev.20200215, and the problem centers around the interaction of the allowSyntheticDefaultImports setting with CommonJS module imports that indirectly involve other CommonJS modules. This results in a TS1192 error stating the absence of a default export despite the configuration's presence in jsconfig.json. The solution suggested involved modifying the module import syntax to fully align with CommonJS procedures, which seemed to resolve the compile-time issue but might cause runtime problems unless esModuleInterop is enabled, as it provides additional compatibility measures.
  • #13626 - Type annotations for default export (62% similar)
    • This issue: No summary available
    • Your issue: The issue highlights a syntax problem with TypeScript type annotations for default exports. The code example attempts to use export default: webpack.Configuration {} which results in a syntax error, indicating that an expression was expected at the location of default:. This suggests a need for TypeScript to support type annotations directly on default exports to improve code readability and reduce boilerplate. Users have proposed syntactic alternatives, such as using export default: SomeType<typeof SomeValue> {} to achieve type safety without repetitive type declarations.

Please check if any of these resolve your issue before proceeding.

RyanCavanaugh avatar Oct 07 '25 17:10 RyanCavanaugh

This error is correct given the information available to TS. See https://github.com/microsoft/TypeScript/wiki/FAQ#the-inferred-type-of-x-cannot-be-named-without-a-reference-to-y-this-is-likely-not-portable-a-type-annotation-is-necessary

There's no reason to have declaration on in an eslint config so I would just turn it off

RyanCavanaugh avatar Oct 07 '25 17:10 RyanCavanaugh

If the Config type were not exported from @eslint/config-helpers I would agree with you, but it is exported. I showed that exporting it with a slightly different syntax allows TS to realize this. So why isn’t TS satisfied with export type Config = import("@eslint/core").ConfigObject?

(I have a large TypeScript project using composite for performance, and declaration cannot be disabled when composite is on.)

andersk avatar Oct 07 '25 17:10 andersk

Like in the FAQ, you're talking about a package path @eslint/core that TS never "sees", thus has no ability to synthesize. You can see this in the error message - the only path TS has seen is @eslint/config-helpers/node_modules/@eslint/core, which is not a portable path

RyanCavanaugh avatar Oct 07 '25 17:10 RyanCavanaugh

(I have a large TypeScript project using composite for performance, and declaration cannot be disabled when composite is on.)

Sure but why is your lint config part of your project in the first place?

RyanCavanaugh avatar Oct 07 '25 17:10 RyanCavanaugh

I’m expecting TS to synthesize

import("@eslint/config-helpers").Config

(like it does successfully with the alternate export syntax), not

import("@eslint/config-helpers/node_modules/@eslint/core").ConfigObject

which is correctly flagged as problematic, but should be unnecessary in the first place.

Sure but why is your lint config part of your project in the first place?

I want all my type checking to be fast—if I were to split it into multiple projects I would still ideally want composite enabled on all of them. (There are other workarounds available; this doesn’t need to be a support question.)

andersk avatar Oct 07 '25 18:10 andersk

There's no reason to have declaration on in an eslint config so I would just turn it off

We're building a shareable config as an npm package which ships with declarations.

Janpot avatar Oct 08 '25 08:10 Janpot

FWIW - this might or might not be a very difficult fix. Whether or not we preserve intermediate symbols during alias resolution is a bit of an implementation detail, so it's possible the information needed to realize that specifier is possible has been lost by the time declaration emit occurs.

RyanCavanaugh avatar Oct 08 '25 15:10 RyanCavanaugh

There's no reason to have declaration on in an eslint config so I would just turn it off

We're building a shareable config as an npm package which ships with declarations.

Running into the same issue building a shareable eslint config.

okdecm avatar Oct 10 '25 16:10 okdecm

Running into the same issue building a shareable eslint config.

I'm running into the same issue not only while building a shareable config, but in regular projects, where eslint configuration file is added to separate tsconfig which uses composite (which as i see is the mainstream approach for typechecking not only project sources, but environmental files).

So +1

egorbabintcev avatar Oct 13 '25 09:10 egorbabintcev