@blumintinc/eslint-plugin-blumint 1.20.131 → 1.20.132
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/lib/index.js
CHANGED
|
@@ -1,2 +1,4 @@
|
|
|
1
1
|
import { TSESLint } from '@typescript-eslint/utils';
|
|
2
|
-
|
|
2
|
+
type MessageIds = 'preferType' | 'preferTypeDefaultExport';
|
|
3
|
+
export declare const preferTypeOverInterface: TSESLint.RuleModule<MessageIds, never[]>;
|
|
4
|
+
export {};
|
|
@@ -47,6 +47,21 @@ function isInsideModuleAugmentation(node) {
|
|
|
47
47
|
}
|
|
48
48
|
return false;
|
|
49
49
|
}
|
|
50
|
+
/**
|
|
51
|
+
* `export default interface X {…}` is the only spelling TypeScript has for a
|
|
52
|
+
* default-exported type: `export default type X = …` is a syntax error in every
|
|
53
|
+
* form, so the keyword swap has no landing site. It lands there anyway because
|
|
54
|
+
* the `TSInterfaceDeclaration`'s range starts at `interface` while
|
|
55
|
+
* `export default` belongs to the parent node, which made `eslint --fix` write
|
|
56
|
+
* a file that no longer parses (#1850).
|
|
57
|
+
*
|
|
58
|
+
* The conversion is still available to the author, as a two-statement
|
|
59
|
+
* restructure a keyword swap cannot express, so the report stands and carries
|
|
60
|
+
* that remedy instead of a fix.
|
|
61
|
+
*/
|
|
62
|
+
function isDefaultExported(node) {
|
|
63
|
+
return node.parent?.type === utils_1.AST_NODE_TYPES.ExportDefaultDeclaration;
|
|
64
|
+
}
|
|
50
65
|
/**
|
|
51
66
|
* Declaration merging is what `interface` can do and `type` cannot, so a name
|
|
52
67
|
* carrying more than one declaration is not a stylistic choice: rewriting one
|
|
@@ -92,6 +107,10 @@ exports.preferTypeOverInterface = (0, createRule_1.createRule)({
|
|
|
92
107
|
preferType: 'Interface "{{interfaceName}}" should be declared as a type alias. ' +
|
|
93
108
|
'Interfaces can merge across declarations and extend in chains, which fragments the resulting shape across files and makes composition harder to predict and trace. ' +
|
|
94
109
|
'Replace `interface` with `type` and use intersections (for example, `type {{interfaceName}} = Base & { field: string }`) to keep the contract closed and predictable.',
|
|
110
|
+
preferTypeDefaultExport: 'Interface "{{interfaceName}}" should be declared as a type alias, but a default-exported interface has no in-place conversion, so no autofix is offered here. ' +
|
|
111
|
+
'Interfaces can merge across declarations and extend in chains, which fragments the resulting shape across files and makes composition harder to predict and trace. ' +
|
|
112
|
+
'TypeScript has no default-exported type alias — `export default type {{interfaceName}} = ...` is a syntax error — so the conversion takes two statements: `type {{interfaceName}} = { field: string };` followed by `export type { {{interfaceName}} as default };`, which keeps every existing `import {{interfaceName}} from ...` working. ' +
|
|
113
|
+
'A named `export type {{interfaceName}} = ...` works too, once the import sites are switched to `import type { {{interfaceName}} }`.',
|
|
95
114
|
},
|
|
96
115
|
fixable: 'code',
|
|
97
116
|
},
|
|
@@ -108,6 +127,22 @@ exports.preferTypeOverInterface = (0, createRule_1.createRule)({
|
|
|
108
127
|
if (isMergedDeclaration(context, node)) {
|
|
109
128
|
return;
|
|
110
129
|
}
|
|
130
|
+
// Withholding the fix rather than emitting a broken one: every rewrite
|
|
131
|
+
// below assumes the `interface` keyword can become `type` where it
|
|
132
|
+
// stands, and under `export default` that position accepts no
|
|
133
|
+
// declaration at all. The report is kept because the author *can* act
|
|
134
|
+
// on it — unlike a merge, the shape converts by hand — and the remedy
|
|
135
|
+
// travels in the message.
|
|
136
|
+
if (isDefaultExported(node)) {
|
|
137
|
+
context.report({
|
|
138
|
+
node,
|
|
139
|
+
messageId: 'preferTypeDefaultExport',
|
|
140
|
+
data: {
|
|
141
|
+
interfaceName: node.id.name,
|
|
142
|
+
},
|
|
143
|
+
});
|
|
144
|
+
return;
|
|
145
|
+
}
|
|
111
146
|
context.report({
|
|
112
147
|
node,
|
|
113
148
|
messageId: 'preferType',
|
package/package.json
CHANGED
package/release-manifest.json
CHANGED
|
@@ -1,4 +1,18 @@
|
|
|
1
1
|
[
|
|
2
|
+
{
|
|
3
|
+
"version": "1.20.132",
|
|
4
|
+
"date": "2026-08-07T12:26:09.881Z",
|
|
5
|
+
"rules": [
|
|
6
|
+
{
|
|
7
|
+
"name": "prefer-type-over-interface",
|
|
8
|
+
"changeType": "fix",
|
|
9
|
+
"issues": [
|
|
10
|
+
1850
|
|
11
|
+
],
|
|
12
|
+
"summary": "decline the fix for a default-exported interface (closes #1850)"
|
|
13
|
+
}
|
|
14
|
+
]
|
|
15
|
+
},
|
|
2
16
|
{
|
|
3
17
|
"version": "1.20.131",
|
|
4
18
|
"date": "2026-08-07T10:28:22.079Z",
|