TS2742 “The inferred type of 'default' cannot be named” with ESLint `defineConfig`
🔎 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.
(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
compositeoption is enabled within atsconfig.jsonfile. This problem arises specifically with theexport defaultsyntax in a code block that involves an inline interface definition. Whencompositeis 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 thecompositesetting.
- #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
allowSyntheticDefaultImportssetting 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 injsconfig.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 unlessesModuleInteropis 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 ofdefault:. 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 usingexport default: SomeType<typeof SomeValue> {}to achieve type safety without repetitive type declarations.
Please check if any of these resolve your issue before proceeding.
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
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.)
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
(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?
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.)
There's no reason to have
declarationon 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.
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.
There's no reason to have
declarationon in an eslint config so I would just turn it offWe're building a shareable config as an npm package which ships with declarations.
Running into the same issue building a shareable eslint config.
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