@typescript-eslint/eslint-plugin 8.47.1-alpha.4 → 8.47.1-alpha.6

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/dist/index.d.ts CHANGED
@@ -764,7 +764,7 @@ declare const _default: {
764
764
  'no-non-null-asserted-optional-chain': import("@typescript-eslint/utils/ts-eslint").RuleModule<"suggestRemovingNonNull" | "noNonNullOptionalChain", [], import("../rules").ESLintPluginDocs, import("@typescript-eslint/utils/ts-eslint").RuleListener>;
765
765
  'no-non-null-assertion': import("@typescript-eslint/utils/ts-eslint").RuleModule<import("./rules/no-non-null-assertion").MessageIds, [], import("../rules").ESLintPluginDocs, import("@typescript-eslint/utils/ts-eslint").RuleListener>;
766
766
  'no-redeclare': import("@typescript-eslint/utils/ts-eslint").RuleModule<import("./rules/no-redeclare").MessageIds, import("./rules/no-redeclare").Options, import("../rules").ESLintPluginDocs, import("@typescript-eslint/utils/ts-eslint").RuleListener>;
767
- 'no-redundant-type-constituents': import("@typescript-eslint/utils/ts-eslint").RuleModule<"overrides" | "errorTypeOverrides" | "literalOverridden" | "overridden" | "primitiveOverridden", [], import("../rules").ESLintPluginDocs, import("@typescript-eslint/utils/ts-eslint").RuleListener>;
767
+ 'no-redundant-type-constituents': import("@typescript-eslint/utils/ts-eslint").RuleModule<"overrides" | "errorTypeOverrides" | "overridden" | "typeOverridden", [], import("../rules").ESLintPluginDocs, import("@typescript-eslint/utils/ts-eslint").RuleListener>;
768
768
  'no-require-imports': import("@typescript-eslint/utils/ts-eslint").RuleModule<"noRequireImports", import("./rules/no-require-imports").Options, import("../rules").ESLintPluginDocs, import("@typescript-eslint/utils/ts-eslint").RuleListener>;
769
769
  'no-restricted-imports': import("@typescript-eslint/utils/ts-eslint").RuleModule<"everything" | "everythingWithCustomMessage" | "importName" | "importNameWithCustomMessage" | "path" | "pathWithCustomMessage" | "patterns" | "patternWithCustomMessage", [import("eslint/lib/rules/no-restricted-imports").ObjectOfPathsAndPatterns] | import("eslint/lib/rules/no-restricted-imports").ArrayOfStringOrObject, import("../rules").ESLintPluginDocs, import("@typescript-eslint/utils/ts-eslint").RuleListener>;
770
770
  'no-restricted-types': import("@typescript-eslint/utils/ts-eslint").RuleModule<import("./rules/no-restricted-types").MessageIds, import("./rules/no-restricted-types").Options, import("../rules").ESLintPluginDocs, import("@typescript-eslint/utils/ts-eslint").RuleListener>;
@@ -787,7 +787,7 @@ declare const _default: {
787
787
  'no-non-null-asserted-optional-chain': TSESLint.RuleModule<"suggestRemovingNonNull" | "noNonNullOptionalChain", [], import("../rules").ESLintPluginDocs, TSESLint.RuleListener>;
788
788
  'no-non-null-assertion': TSESLint.RuleModule<import("./rules/no-non-null-assertion").MessageIds, [], import("../rules").ESLintPluginDocs, TSESLint.RuleListener>;
789
789
  'no-redeclare': TSESLint.RuleModule<import("./rules/no-redeclare").MessageIds, import("./rules/no-redeclare").Options, import("../rules").ESLintPluginDocs, TSESLint.RuleListener>;
790
- 'no-redundant-type-constituents': TSESLint.RuleModule<"overrides" | "errorTypeOverrides" | "literalOverridden" | "overridden" | "primitiveOverridden", [], import("../rules").ESLintPluginDocs, TSESLint.RuleListener>;
790
+ 'no-redundant-type-constituents': TSESLint.RuleModule<"overrides" | "errorTypeOverrides" | "overridden" | "typeOverridden", [], import("../rules").ESLintPluginDocs, TSESLint.RuleListener>;
791
791
  'no-require-imports': TSESLint.RuleModule<"noRequireImports", import("./rules/no-require-imports").Options, import("../rules").ESLintPluginDocs, TSESLint.RuleListener>;
792
792
  'no-restricted-imports': TSESLint.RuleModule<"everything" | "everythingWithCustomMessage" | "importName" | "importNameWithCustomMessage" | "path" | "pathWithCustomMessage" | "patterns" | "patternWithCustomMessage", [import("eslint/lib/rules/no-restricted-imports").ObjectOfPathsAndPatterns] | import("eslint/lib/rules/no-restricted-imports").ArrayOfStringOrObject, import("../rules").ESLintPluginDocs, TSESLint.RuleListener>;
793
793
  'no-restricted-types': TSESLint.RuleModule<import("./rules/no-restricted-types").MessageIds, import("./rules/no-restricted-types").Options, import("../rules").ESLintPluginDocs, TSESLint.RuleListener>;
@@ -92,7 +92,7 @@ declare const rules: {
92
92
  'no-non-null-asserted-optional-chain': import("@typescript-eslint/utils/ts-eslint").RuleModule<"suggestRemovingNonNull" | "noNonNullOptionalChain", [], import("../../rules").ESLintPluginDocs, import("@typescript-eslint/utils/ts-eslint").RuleListener>;
93
93
  'no-non-null-assertion': import("@typescript-eslint/utils/ts-eslint").RuleModule<import("./no-non-null-assertion").MessageIds, [], import("../../rules").ESLintPluginDocs, import("@typescript-eslint/utils/ts-eslint").RuleListener>;
94
94
  'no-redeclare': import("@typescript-eslint/utils/ts-eslint").RuleModule<import("./no-redeclare").MessageIds, import("./no-redeclare").Options, import("../../rules").ESLintPluginDocs, import("@typescript-eslint/utils/ts-eslint").RuleListener>;
95
- 'no-redundant-type-constituents': import("@typescript-eslint/utils/ts-eslint").RuleModule<"overrides" | "errorTypeOverrides" | "literalOverridden" | "overridden" | "primitiveOverridden", [], import("../../rules").ESLintPluginDocs, import("@typescript-eslint/utils/ts-eslint").RuleListener>;
95
+ 'no-redundant-type-constituents': import("@typescript-eslint/utils/ts-eslint").RuleModule<"overrides" | "errorTypeOverrides" | "overridden" | "typeOverridden", [], import("../../rules").ESLintPluginDocs, import("@typescript-eslint/utils/ts-eslint").RuleListener>;
96
96
  'no-require-imports': import("@typescript-eslint/utils/ts-eslint").RuleModule<"noRequireImports", import("./no-require-imports").Options, import("../../rules").ESLintPluginDocs, import("@typescript-eslint/utils/ts-eslint").RuleListener>;
97
97
  'no-restricted-imports': import("@typescript-eslint/utils/ts-eslint").RuleModule<"everything" | "everythingWithCustomMessage" | "importName" | "importNameWithCustomMessage" | "path" | "pathWithCustomMessage" | "patterns" | "patternWithCustomMessage", [import("eslint/lib/rules/no-restricted-imports").ObjectOfPathsAndPatterns] | import("eslint/lib/rules/no-restricted-imports").ArrayOfStringOrObject, import("../../rules").ESLintPluginDocs, import("@typescript-eslint/utils/ts-eslint").RuleListener>;
98
98
  'no-restricted-types': import("@typescript-eslint/utils/ts-eslint").RuleModule<import("./no-restricted-types").MessageIds, import("./no-restricted-types").Options, import("../../rules").ESLintPluginDocs, import("@typescript-eslint/utils/ts-eslint").RuleListener>;
@@ -1,2 +1,2 @@
1
- declare const _default: import("@typescript-eslint/utils/ts-eslint").RuleModule<"overrides" | "errorTypeOverrides" | "literalOverridden" | "overridden" | "primitiveOverridden", [], import("../../rules").ESLintPluginDocs, import("@typescript-eslint/utils/ts-eslint").RuleListener>;
1
+ declare const _default: import("@typescript-eslint/utils/ts-eslint").RuleModule<"overrides" | "errorTypeOverrides" | "overridden" | "typeOverridden", [], import("../../rules").ESLintPluginDocs, import("@typescript-eslint/utils/ts-eslint").RuleListener>;
2
2
  export default _default;
@@ -37,47 +37,6 @@ const utils_1 = require("@typescript-eslint/utils");
37
37
  const tsutils = __importStar(require("ts-api-utils"));
38
38
  const ts = __importStar(require("typescript"));
39
39
  const util_1 = require("../util");
40
- const literalToPrimitiveTypeFlags = {
41
- [ts.TypeFlags.BigIntLiteral]: ts.TypeFlags.BigInt,
42
- [ts.TypeFlags.BooleanLiteral]: ts.TypeFlags.Boolean,
43
- [ts.TypeFlags.NumberLiteral]: ts.TypeFlags.Number,
44
- [ts.TypeFlags.StringLiteral]: ts.TypeFlags.String,
45
- [ts.TypeFlags.TemplateLiteral]: ts.TypeFlags.String,
46
- };
47
- const literalTypeFlags = [
48
- ts.TypeFlags.BigIntLiteral,
49
- ts.TypeFlags.BooleanLiteral,
50
- ts.TypeFlags.NumberLiteral,
51
- ts.TypeFlags.StringLiteral,
52
- ts.TypeFlags.TemplateLiteral,
53
- ];
54
- const primitiveTypeFlags = [
55
- ts.TypeFlags.BigInt,
56
- ts.TypeFlags.Boolean,
57
- ts.TypeFlags.Number,
58
- ts.TypeFlags.String,
59
- ];
60
- const primitiveTypeFlagNames = {
61
- [ts.TypeFlags.BigInt]: 'bigint',
62
- [ts.TypeFlags.Boolean]: 'boolean',
63
- [ts.TypeFlags.Number]: 'number',
64
- [ts.TypeFlags.String]: 'string',
65
- };
66
- const primitiveTypeFlagTypes = {
67
- bigint: ts.TypeFlags.BigIntLiteral,
68
- boolean: ts.TypeFlags.BooleanLiteral,
69
- number: ts.TypeFlags.NumberLiteral,
70
- string: ts.TypeFlags.StringLiteral,
71
- };
72
- const keywordNodeTypesToTsTypes = new Map([
73
- [utils_1.TSESTree.AST_NODE_TYPES.TSAnyKeyword, ts.TypeFlags.Any],
74
- [utils_1.TSESTree.AST_NODE_TYPES.TSBigIntKeyword, ts.TypeFlags.BigInt],
75
- [utils_1.TSESTree.AST_NODE_TYPES.TSBooleanKeyword, ts.TypeFlags.Boolean],
76
- [utils_1.TSESTree.AST_NODE_TYPES.TSNeverKeyword, ts.TypeFlags.Never],
77
- [utils_1.TSESTree.AST_NODE_TYPES.TSNumberKeyword, ts.TypeFlags.Number],
78
- [utils_1.TSESTree.AST_NODE_TYPES.TSStringKeyword, ts.TypeFlags.String],
79
- [utils_1.TSESTree.AST_NODE_TYPES.TSUnknownKeyword, ts.TypeFlags.Unknown],
80
- ]);
81
40
  function addToMapGroup(map, key, value) {
82
41
  const existing = map.get(key);
83
42
  if (existing) {
@@ -87,88 +46,270 @@ function addToMapGroup(map, key, value) {
87
46
  map.set(key, [value]);
88
47
  }
89
48
  }
90
- function describeLiteralType(type) {
91
- if (type.isStringLiteral()) {
92
- return JSON.stringify(type.value);
49
+ function isUnionNodeInsideReturnType(node) {
50
+ if (node.parent.type === utils_1.AST_NODE_TYPES.TSUnionType) {
51
+ return isUnionNodeInsideReturnType(node.parent);
93
52
  }
94
- if ((0, util_1.isTypeBigIntLiteralType)(type)) {
95
- return `${type.value.negative ? '-' : ''}${type.value.base10Value}n`;
53
+ return (node.parent.type === utils_1.AST_NODE_TYPES.TSTypeAnnotation &&
54
+ (0, util_1.isFunctionOrFunctionType)(node.parent.parent));
55
+ }
56
+ function isObjectOrIntersectionType(type) {
57
+ return tsutils.isObjectType(type) || tsutils.isIntersectionType(type);
58
+ }
59
+ function shouldCheckTypeRedundancy(type, checker, depth = 0) {
60
+ if (depth > 10) {
61
+ return false;
96
62
  }
97
- if (type.isLiteral()) {
98
- // eslint-disable-next-line @typescript-eslint/no-base-to-string
99
- return type.value.toString();
63
+ if (tsutils.isObjectType(type)) {
64
+ const symbol = type.getSymbol();
65
+ if (checker.isArrayLikeType(type)) {
66
+ return true;
67
+ }
68
+ if (!symbol) {
69
+ return false;
70
+ }
71
+ const declarations = symbol.getDeclarations();
72
+ const declaration = declarations?.[0];
73
+ if (!declaration) {
74
+ return false;
75
+ }
76
+ if (declaration.kind !== ts.SyntaxKind.TypeLiteral &&
77
+ declaration.kind !== ts.SyntaxKind.InterfaceDeclaration &&
78
+ declaration.kind !== ts.SyntaxKind.MappedType) {
79
+ return false;
80
+ }
100
81
  }
101
- if (tsutils.isIntrinsicErrorType(type) && type.aliasSymbol) {
102
- return type.aliasSymbol.escapedName.toString();
82
+ if (isObjectOrIntersectionType(type)) {
83
+ const props = type.getProperties();
84
+ for (const prop of props) {
85
+ const type = checker.getTypeOfSymbol(prop);
86
+ if (!shouldCheckTypeRedundancy(type, checker, depth + 1)) {
87
+ return false;
88
+ }
89
+ }
90
+ return true;
103
91
  }
104
- if ((0, util_1.isTypeAnyType)(type)) {
105
- return 'any';
92
+ if (tsutils.isUnionType(type)) {
93
+ return type.types.every(typePart => shouldCheckTypeRedundancy(typePart, checker, depth));
106
94
  }
107
- if ((0, util_1.isTypeNeverType)(type)) {
108
- return 'never';
95
+ return true;
96
+ }
97
+ function isTargetTypeRedundantInIntersection(sourceType, targetType, checker) {
98
+ if (!shouldCheckTypeRedundancy(sourceType, checker) ||
99
+ !shouldCheckTypeRedundancy(targetType, checker)) {
100
+ return false;
109
101
  }
110
- if ((0, util_1.isTypeUnknownType)(type)) {
111
- return 'unknown';
102
+ if (tsutils.isUnionType(sourceType)) {
103
+ for (const typePart of sourceType.types) {
104
+ if (!tsutils.isObjectType(typePart)) {
105
+ continue;
106
+ }
107
+ const isRedundant = isTargetTypeRedundantInIntersection(typePart, targetType, checker);
108
+ if (!isRedundant) {
109
+ return false;
110
+ }
111
+ }
112
+ return checker.isTypeAssignableTo(sourceType, targetType);
112
113
  }
113
- if ((0, util_1.isTypeTemplateLiteralType)(type)) {
114
- return 'template literal type';
114
+ if (tsutils.isUnionType(targetType) &&
115
+ !tsutils.isIntrinsicBooleanType(targetType)) {
116
+ for (const typePart of targetType.types) {
117
+ if (!tsutils.isObjectType(typePart)) {
118
+ continue;
119
+ }
120
+ const isRedundant = isTargetTypeRedundantInIntersection(sourceType, typePart, checker);
121
+ if (!isRedundant) {
122
+ return false;
123
+ }
124
+ }
125
+ return checker.isTypeAssignableTo(sourceType, targetType);
115
126
  }
116
- if ((0, util_1.isTypeBigIntLiteralType)(type)) {
117
- return `${type.value.negative ? '-' : ''}${type.value.base10Value}n`;
127
+ if (checker.isTupleType(targetType)) {
128
+ if (checker.isTupleType(sourceType)) {
129
+ const sourceArguments = sourceType.typeArguments;
130
+ const targetArguments = targetType.typeArguments;
131
+ if (!sourceArguments || !targetArguments) {
132
+ return false;
133
+ }
134
+ if (targetArguments.length !== sourceArguments.length) {
135
+ return false;
136
+ }
137
+ for (let i = 0; i < targetArguments.length; ++i) {
138
+ const sourceTypeArgument = sourceArguments[i];
139
+ const targetTypeArgument = targetArguments[i];
140
+ if (!isTargetTypeRedundantInIntersection(sourceTypeArgument, targetTypeArgument, checker)) {
141
+ return false;
142
+ }
143
+ }
144
+ return true;
145
+ }
146
+ return false;
118
147
  }
119
- if (tsutils.isTrueLiteralType(type)) {
120
- return 'true';
148
+ if (checker.isArrayType(targetType)) {
149
+ if (checker.isArrayType(sourceType)) {
150
+ const sourceArgumentType = sourceType.typeArguments?.[0];
151
+ const targetArgumentType = targetType.typeArguments?.[0];
152
+ if (!sourceArgumentType || !targetArgumentType) {
153
+ return false;
154
+ }
155
+ return isTargetTypeRedundantInIntersection(sourceArgumentType, targetArgumentType, checker);
156
+ }
157
+ if (checker.isTupleType(sourceType)) {
158
+ const targetArgumentType = targetType.typeArguments?.[0];
159
+ if (!targetArgumentType) {
160
+ return false;
161
+ }
162
+ const sourceArgumentTypes = sourceType.typeArguments;
163
+ if (!sourceArgumentTypes) {
164
+ return true;
165
+ }
166
+ for (const sourceTypeArgument of sourceArgumentTypes) {
167
+ if (!isTargetTypeRedundantInIntersection(sourceTypeArgument, targetArgumentType, checker)) {
168
+ return false;
169
+ }
170
+ }
171
+ return true;
172
+ }
173
+ return false;
121
174
  }
122
- if (tsutils.isFalseLiteralType(type)) {
123
- return 'false';
175
+ if (tsutils.isObjectType(sourceType) && tsutils.isObjectType(targetType)) {
176
+ const sourceProps = sourceType.getProperties();
177
+ const targetProps = targetType.getProperties();
178
+ if (targetProps.length === 0) {
179
+ return false;
180
+ }
181
+ for (const targetProp of targetProps) {
182
+ const sourceProp = sourceProps.find(prop => prop.getName() === targetProp.getName());
183
+ if (!sourceProp) {
184
+ return false;
185
+ }
186
+ const sourcePropType = checker.getTypeOfSymbol(sourceProp);
187
+ const targetPropType = checker.getTypeOfSymbol(targetProp);
188
+ const targetPropTypeIsRedundant = isTargetTypeRedundantInIntersection(sourcePropType, targetPropType, checker);
189
+ if (!targetPropTypeIsRedundant) {
190
+ return false;
191
+ }
192
+ }
193
+ return true;
124
194
  }
125
- return 'literal type';
195
+ return checker.isTypeAssignableTo(sourceType, targetType);
126
196
  }
127
- function describeLiteralTypeNode(typeNode) {
128
- switch (typeNode.type) {
129
- case utils_1.AST_NODE_TYPES.TSAnyKeyword:
130
- return 'any';
131
- case utils_1.AST_NODE_TYPES.TSBooleanKeyword:
132
- return 'boolean';
133
- case utils_1.AST_NODE_TYPES.TSNeverKeyword:
134
- return 'never';
135
- case utils_1.AST_NODE_TYPES.TSNumberKeyword:
136
- return 'number';
137
- case utils_1.AST_NODE_TYPES.TSStringKeyword:
138
- return 'string';
139
- case utils_1.AST_NODE_TYPES.TSUnknownKeyword:
140
- return 'unknown';
141
- case utils_1.AST_NODE_TYPES.TSLiteralType:
142
- switch (typeNode.literal.type) {
143
- case utils_1.TSESTree.AST_NODE_TYPES.Literal:
144
- switch (typeof typeNode.literal.value) {
145
- case 'bigint':
146
- return `${typeNode.literal.value < 0 ? '-' : ''}${typeNode.literal.value}n`;
147
- case 'string':
148
- return JSON.stringify(typeNode.literal.value);
149
- default:
150
- return `${typeNode.literal.value}`;
151
- }
152
- case utils_1.TSESTree.AST_NODE_TYPES.TemplateLiteral:
153
- return 'template literal type';
197
+ function isTargetTypeRedundantInUnion(sourceType, targetType, checker) {
198
+ if (!shouldCheckTypeRedundancy(sourceType, checker) ||
199
+ !shouldCheckTypeRedundancy(targetType, checker)) {
200
+ return false;
201
+ }
202
+ if (tsutils.isUnionType(targetType) &&
203
+ !tsutils.isIntrinsicBooleanType(targetType)) {
204
+ for (const typePart of targetType.types) {
205
+ const isRedundant = isTargetTypeRedundantInUnion(sourceType, typePart, checker);
206
+ if (!isRedundant) {
207
+ return false;
208
+ }
209
+ }
210
+ return true;
211
+ }
212
+ if (tsutils.isUnionType(sourceType)) {
213
+ for (const typePart of sourceType.types) {
214
+ const isRedundant = isTargetTypeRedundantInUnion(typePart, targetType, checker);
215
+ if (isRedundant) {
216
+ return true;
154
217
  }
218
+ }
219
+ return false;
155
220
  }
156
- return 'literal type';
221
+ if (checker.isTupleType(targetType)) {
222
+ if (checker.isArrayType(sourceType)) {
223
+ const sourceArgumentType = sourceType.typeArguments?.[0];
224
+ if (!sourceArgumentType) {
225
+ return false;
226
+ }
227
+ const targetArguments = targetType.typeArguments;
228
+ if (!targetArguments) {
229
+ return true;
230
+ }
231
+ for (const targetTypeArgument of targetArguments) {
232
+ if (!isTargetTypeRedundantInUnion(sourceArgumentType, targetTypeArgument, checker)) {
233
+ return false;
234
+ }
235
+ }
236
+ return true;
237
+ }
238
+ if (checker.isTupleType(sourceType)) {
239
+ const sourceArguments = sourceType.typeArguments;
240
+ const targetArguments = targetType.typeArguments;
241
+ if (!sourceArguments || !targetArguments) {
242
+ return false;
243
+ }
244
+ if (targetArguments.length !== sourceArguments.length) {
245
+ return false;
246
+ }
247
+ for (let i = 0; i < targetArguments.length; ++i) {
248
+ const sourceTypeArgument = sourceArguments[i];
249
+ const targetTypeArgument = targetArguments[i];
250
+ if (!isTargetTypeRedundantInUnion(sourceTypeArgument, targetTypeArgument, checker)) {
251
+ return false;
252
+ }
253
+ }
254
+ return true;
255
+ }
256
+ return false;
257
+ }
258
+ if (checker.isArrayType(sourceType) && checker.isArrayType(targetType)) {
259
+ const sourceArgumentType = sourceType.typeArguments?.[0];
260
+ const targetArgumentType = targetType.typeArguments?.[0];
261
+ if (!sourceArgumentType || !targetArgumentType) {
262
+ return false;
263
+ }
264
+ return isTargetTypeRedundantInUnion(sourceArgumentType, targetArgumentType, checker);
265
+ }
266
+ if (isObjectOrIntersectionType(sourceType) &&
267
+ isObjectOrIntersectionType(targetType)) {
268
+ const sourceProps = sourceType.getProperties();
269
+ const targetProps = targetType.getProperties();
270
+ if (sourceProps.length !== targetProps.length) {
271
+ return false;
272
+ }
273
+ if (targetProps.length === 0) {
274
+ return false;
275
+ }
276
+ for (const targetProp of targetProps) {
277
+ const sourceProp = sourceProps.find(prop => prop.getName() === targetProp.getName());
278
+ if (!sourceProp) {
279
+ return false;
280
+ }
281
+ const sourcePropType = checker.getTypeOfSymbol(sourceProp);
282
+ const targetPropType = checker.getTypeOfSymbol(targetProp);
283
+ const targetPropTypeIsRedundant = isTargetTypeRedundantInUnion(sourcePropType, targetPropType, checker);
284
+ if (!targetPropTypeIsRedundant) {
285
+ return false;
286
+ }
287
+ }
288
+ return true;
289
+ }
290
+ return checker.isTypeAssignableTo(targetType, sourceType);
157
291
  }
158
- function isNodeInsideReturnType(node) {
159
- return (node.parent.type === utils_1.AST_NODE_TYPES.TSTypeAnnotation &&
160
- (0, util_1.isFunctionOrFunctionType)(node.parent.parent));
292
+ function mergeTypeNames(typeWithNames, operator) {
293
+ if (typeWithNames.length === 1) {
294
+ return typeWithNames[0].typeName;
295
+ }
296
+ const wrapType = (typeWithName) => {
297
+ if (operator === '|' && typeWithName.type.isIntersection()) {
298
+ return `(${typeWithName.typeName})`;
299
+ }
300
+ if (operator === '&' && typeWithName.type.isUnion()) {
301
+ return `(${typeWithName.typeName})`;
302
+ }
303
+ return typeWithName.typeName;
304
+ };
305
+ return typeWithNames.map(wrapType).join(` ${operator} `);
161
306
  }
162
- /**
163
- * @remarks TypeScript stores boolean types as the union false | true, always.
164
- */
165
- function unionTypePartsUnlessBoolean(type) {
166
- return type.isUnion() &&
167
- type.types.length === 2 &&
168
- tsutils.isFalseLiteralType(type.types[0]) &&
169
- tsutils.isTrueLiteralType(type.types[1])
170
- ? [type]
171
- : tsutils.unionConstituents(type);
307
+ function getGroupTypeRelationsByNonRedundantType(typeRedundancyRelations) {
308
+ const groups = new Map();
309
+ for (const typeRedundancyRelation of typeRedundancyRelations) {
310
+ addToMapGroup(groups, typeRedundancyRelation.nonRedundantTypeWithName.type, typeRedundancyRelation);
311
+ }
312
+ return groups;
172
313
  }
173
314
  exports.default = (0, util_1.createRule)({
174
315
  name: 'no-redundant-type-constituents',
@@ -181,61 +322,68 @@ exports.default = (0, util_1.createRule)({
181
322
  },
182
323
  messages: {
183
324
  errorTypeOverrides: `'{{typeName}}' is an 'error' type that acts as 'any' and overrides all other types in this {{container}} type.`,
184
- literalOverridden: `{{literal}} is overridden by {{primitive}} in this union type.`,
185
325
  overridden: `'{{typeName}}' is overridden by other types in this {{container}} type.`,
186
326
  overrides: `'{{typeName}}' overrides all other types in this {{container}} type.`,
187
- primitiveOverridden: `{{primitive}} is overridden by the {{literal}} in this intersection type.`,
327
+ typeOverridden: `{{redundantType}} is overridden by {{nonRedundantType}} in this {{container}} type.`,
188
328
  },
189
329
  schema: [],
190
330
  },
191
331
  defaultOptions: [],
192
332
  create(context) {
193
333
  const services = (0, util_1.getParserServices)(context);
194
- const typesCache = new Map();
195
- function getTypeNodeTypePartFlags(typeNode) {
196
- const keywordTypeFlags = keywordNodeTypesToTsTypes.get(typeNode.type);
197
- if (keywordTypeFlags) {
198
- return [
199
- {
200
- typeFlags: keywordTypeFlags,
201
- typeName: describeLiteralTypeNode(typeNode),
202
- },
203
- ];
334
+ const checker = services.program.getTypeChecker();
335
+ function reportRedundantTypes(redundantTypes, container) {
336
+ for (const [typeNode, typeRelations] of redundantTypes) {
337
+ const groupTypeRelationsByNonRedundantType = getGroupTypeRelationsByNonRedundantType(typeRelations);
338
+ for (const [nonRedundantType, typeRelationAndNames,] of groupTypeRelationsByNonRedundantType) {
339
+ const nonRedundantTypeName = checker.typeToString(nonRedundantType);
340
+ const mergedRedundantTypeName = mergeTypeNames(typeRelationAndNames.map(({ redundantTypeWithName }) => redundantTypeWithName), container === 'union' ? '|' : '&');
341
+ context.report({
342
+ node: typeNode,
343
+ messageId: 'typeOverridden',
344
+ data: {
345
+ container,
346
+ nonRedundantType: nonRedundantTypeName,
347
+ redundantType: mergedRedundantTypeName,
348
+ },
349
+ });
350
+ }
204
351
  }
205
- if (typeNode.type === utils_1.AST_NODE_TYPES.TSLiteralType &&
206
- typeNode.literal.type === utils_1.AST_NODE_TYPES.Literal) {
207
- return [
208
- {
209
- typeFlags: primitiveTypeFlagTypes[typeof typeNode.literal
210
- .value],
211
- typeName: describeLiteralTypeNode(typeNode),
212
- },
213
- ];
352
+ }
353
+ function getUnionTypePart(typeNode, checker) {
354
+ if (ts.isParenthesizedTypeNode(typeNode)) {
355
+ return getUnionTypePart(typeNode.type, checker);
214
356
  }
215
- if (typeNode.type === utils_1.AST_NODE_TYPES.TSUnionType) {
216
- return typeNode.types.flatMap(getTypeNodeTypePartFlags);
357
+ if (ts.isUnionTypeNode(typeNode)) {
358
+ return typeNode.types.flatMap(typeNode => getUnionTypePart(typeNode, checker));
217
359
  }
218
- const nodeType = services.getTypeAtLocation(typeNode);
219
- const typeParts = unionTypePartsUnlessBoolean(nodeType);
220
- return typeParts.map(typePart => ({
221
- typeFlags: typePart.flags,
222
- typeName: describeLiteralType(typePart),
223
- }));
360
+ const type = checker.getTypeAtLocation(typeNode);
361
+ return [
362
+ {
363
+ type,
364
+ typeName: checker.typeToString(type),
365
+ },
366
+ ];
224
367
  }
225
- function getTypeNodeTypePartFlagsCached(typeNode) {
226
- const existing = typesCache.get(typeNode);
227
- if (existing) {
228
- return existing;
368
+ function getIntersectionTypePart(typeNode, checker) {
369
+ if (ts.isParenthesizedTypeNode(typeNode)) {
370
+ return getIntersectionTypePart(typeNode.type, checker);
371
+ }
372
+ if (ts.isIntersectionTypeNode(typeNode)) {
373
+ return typeNode.types.flatMap(typeNode => getIntersectionTypePart(typeNode, checker));
229
374
  }
230
- const created = getTypeNodeTypePartFlags(typeNode);
231
- typesCache.set(typeNode, created);
232
- return created;
375
+ const type = checker.getTypeAtLocation(typeNode);
376
+ return [
377
+ {
378
+ type,
379
+ typeName: checker.typeToString(type),
380
+ },
381
+ ];
233
382
  }
234
383
  return {
235
- 'TSIntersectionType:exit'(node) {
236
- const seenLiteralTypes = new Map();
237
- const seenPrimitiveTypes = new Map();
238
- const seenUnionTypes = new Map();
384
+ TSIntersectionType(node) {
385
+ const seenTypes = new Set();
386
+ const redundantTypes = new Map();
239
387
  function checkIntersectionBottomAndTopTypes({ typeFlags, typeName }, typeNode) {
240
388
  for (const [messageId, checkFlag] of [
241
389
  ['overrides', ts.TypeFlags.Any],
@@ -259,87 +407,70 @@ exports.default = (0, util_1.createRule)({
259
407
  return false;
260
408
  }
261
409
  for (const typeNode of node.types) {
262
- const typePartFlags = getTypeNodeTypePartFlagsCached(typeNode);
263
- for (const typePart of typePartFlags) {
264
- if (checkIntersectionBottomAndTopTypes(typePart, typeNode)) {
410
+ const tsTypeNode = services.esTreeNodeToTSNodeMap.get(typeNode);
411
+ const typeParts = getIntersectionTypePart(tsTypeNode, checker);
412
+ for (const typePart of typeParts) {
413
+ if (typePart.type.flags & ts.TypeFlags.Never ||
414
+ typePart.type.flags & ts.TypeFlags.Any ||
415
+ typePart.type.flags & ts.TypeFlags.Unknown) {
416
+ checkIntersectionBottomAndTopTypes({
417
+ typeFlags: typePart.type.flags,
418
+ typeName: typePart.typeName,
419
+ }, typeNode);
265
420
  continue;
266
421
  }
267
- for (const literalTypeFlag of literalTypeFlags) {
268
- if (typePart.typeFlags === literalTypeFlag) {
269
- addToMapGroup(seenLiteralTypes, literalToPrimitiveTypeFlags[literalTypeFlag], typePart.typeName);
270
- break;
422
+ const { type: targetType, typeName: targetTypeName } = typePart;
423
+ for (const seenType of seenTypes) {
424
+ const { type: sourceType, parentTypeNode, typeName: sourceTypeName, } = seenType;
425
+ const targetTypeIsRedundant = isTargetTypeRedundantInIntersection(sourceType, targetType, checker);
426
+ const sourceTypeIsRedundant = isTargetTypeRedundantInIntersection(targetType, sourceType, checker);
427
+ if (targetTypeIsRedundant &&
428
+ targetTypeIsRedundant === sourceTypeIsRedundant) {
429
+ continue;
271
430
  }
272
- }
273
- for (const primitiveTypeFlag of primitiveTypeFlags) {
274
- if (typePart.typeFlags === primitiveTypeFlag) {
275
- addToMapGroup(seenPrimitiveTypes, primitiveTypeFlag, typeNode);
276
- }
277
- }
278
- }
279
- // if any typeNode is TSTypeReference and typePartFlags have more than 1 element, than the referenced type is definitely a union.
280
- if (typePartFlags.length >= 2) {
281
- seenUnionTypes.set(typeNode, typePartFlags);
282
- }
283
- }
284
- /**
285
- * @example
286
- * ```ts
287
- * type F = "a"|2|"b";
288
- * type I = F & string;
289
- * ```
290
- * This function checks if all the union members of `F` are assignable to the other member of `I`. If every member is assignable, then its reported else not.
291
- */
292
- const checkIfUnionsAreAssignable = () => {
293
- for (const [typeRef, typeValues] of seenUnionTypes) {
294
- let primitive = undefined;
295
- for (const { typeFlags } of typeValues) {
296
- if (seenPrimitiveTypes.has(literalToPrimitiveTypeFlags[typeFlags])) {
297
- primitive =
298
- literalToPrimitiveTypeFlags[typeFlags];
431
+ if (sourceTypeIsRedundant) {
432
+ addToMapGroup(redundantTypes, parentTypeNode, {
433
+ nonRedundantTypeWithName: {
434
+ type: targetType,
435
+ typeName: targetTypeName,
436
+ },
437
+ redundantTypeWithName: {
438
+ type: sourceType,
439
+ typeName: sourceTypeName,
440
+ },
441
+ });
299
442
  }
300
- else {
301
- primitive = undefined;
302
- break;
443
+ if (targetTypeIsRedundant) {
444
+ addToMapGroup(redundantTypes, typeNode, {
445
+ nonRedundantTypeWithName: {
446
+ type: sourceType,
447
+ typeName: sourceTypeName,
448
+ },
449
+ redundantTypeWithName: {
450
+ type: targetType,
451
+ typeName: targetTypeName,
452
+ },
453
+ });
303
454
  }
304
455
  }
305
- if (Number.isInteger(primitive)) {
306
- context.report({
307
- node: typeRef,
308
- messageId: 'primitiveOverridden',
309
- data: {
310
- literal: typeValues.map(name => name.typeName).join(' | '),
311
- primitive: primitiveTypeFlagNames[primitive],
312
- },
313
- });
314
- }
315
456
  }
316
- };
317
- if (seenUnionTypes.size > 0) {
318
- checkIfUnionsAreAssignable();
319
- return;
320
- }
321
- // For each primitive type of all the seen primitive types,
322
- // if there was a literal type seen that overrides it,
323
- // report each of the primitive type's type nodes
324
- for (const [primitiveTypeFlag, typeNodes] of seenPrimitiveTypes) {
325
- const matchedLiteralTypes = seenLiteralTypes.get(primitiveTypeFlag);
326
- if (matchedLiteralTypes) {
327
- for (const typeNode of typeNodes) {
328
- context.report({
329
- node: typeNode,
330
- messageId: 'primitiveOverridden',
331
- data: {
332
- literal: matchedLiteralTypes.join(' | '),
333
- primitive: primitiveTypeFlagNames[primitiveTypeFlag],
334
- },
335
- });
457
+ for (const typePart of typeParts) {
458
+ if (typePart.type.flags === ts.TypeFlags.Any ||
459
+ typePart.type.flags === ts.TypeFlags.Unknown ||
460
+ typePart.type.flags === ts.TypeFlags.Never) {
461
+ continue;
336
462
  }
463
+ seenTypes.add({
464
+ ...typePart,
465
+ parentTypeNode: typeNode,
466
+ });
337
467
  }
338
468
  }
469
+ reportRedundantTypes(redundantTypes, 'intersection');
339
470
  },
340
- 'TSUnionType:exit'(node) {
341
- const seenLiteralTypes = new Map();
342
- const seenPrimitiveTypes = new Set();
471
+ TSUnionType(node) {
472
+ const seenTypes = new Set();
473
+ const redundantTypes = new Map();
343
474
  function checkUnionBottomAndTopTypes({ typeFlags, typeName }, typeNode) {
344
475
  for (const checkFlag of [
345
476
  ts.TypeFlags.Any,
@@ -360,7 +491,7 @@ exports.default = (0, util_1.createRule)({
360
491
  }
361
492
  }
362
493
  if (typeFlags === ts.TypeFlags.Never &&
363
- !isNodeInsideReturnType(node)) {
494
+ !isUnionNodeInsideReturnType(node)) {
364
495
  context.report({
365
496
  node: typeNode,
366
497
  messageId: 'overridden',
@@ -374,57 +505,66 @@ exports.default = (0, util_1.createRule)({
374
505
  return false;
375
506
  }
376
507
  for (const typeNode of node.types) {
377
- const typePartFlags = getTypeNodeTypePartFlagsCached(typeNode);
378
- for (const typePart of typePartFlags) {
379
- if (checkUnionBottomAndTopTypes(typePart, typeNode)) {
508
+ const tsTypeNode = services.esTreeNodeToTSNodeMap.get(typeNode);
509
+ const typeParts = getUnionTypePart(tsTypeNode, checker);
510
+ for (const typePart of typeParts) {
511
+ if (typePart.type.flags & ts.TypeFlags.Never ||
512
+ typePart.type.flags & ts.TypeFlags.Any ||
513
+ typePart.type.flags & ts.TypeFlags.Unknown) {
514
+ checkUnionBottomAndTopTypes({
515
+ typeFlags: typePart.type.flags,
516
+ typeName: typePart.typeName,
517
+ }, typeNode);
380
518
  continue;
381
519
  }
382
- for (const literalTypeFlag of literalTypeFlags) {
383
- if (typePart.typeFlags === literalTypeFlag) {
384
- addToMapGroup(seenLiteralTypes, literalToPrimitiveTypeFlags[literalTypeFlag], {
385
- literalValue: typePart.typeName,
386
- typeNode,
520
+ const { type: targetType, typeName: targetTypeName } = typePart;
521
+ for (const seenType of seenTypes) {
522
+ const { type: sourceType, parentTypeNode, typeName: sourceTypeName, } = seenType;
523
+ const targetTypeIsRedundant = isTargetTypeRedundantInUnion(sourceType, targetType, checker);
524
+ const sourceTypeIsRedundant = isTargetTypeRedundantInUnion(targetType, sourceType, checker);
525
+ if (targetTypeIsRedundant &&
526
+ targetTypeIsRedundant === sourceTypeIsRedundant) {
527
+ continue;
528
+ }
529
+ if (sourceTypeIsRedundant) {
530
+ addToMapGroup(redundantTypes, parentTypeNode, {
531
+ nonRedundantTypeWithName: {
532
+ type: targetType,
533
+ typeName: targetTypeName,
534
+ },
535
+ redundantTypeWithName: {
536
+ type: sourceType,
537
+ typeName: sourceTypeName,
538
+ },
387
539
  });
388
- break;
389
540
  }
390
- }
391
- for (const primitiveTypeFlag of primitiveTypeFlags) {
392
- if ((typePart.typeFlags & primitiveTypeFlag) !== 0) {
393
- seenPrimitiveTypes.add(primitiveTypeFlag);
541
+ if (targetTypeIsRedundant) {
542
+ addToMapGroup(redundantTypes, typeNode, {
543
+ nonRedundantTypeWithName: {
544
+ type: sourceType,
545
+ typeName: sourceTypeName,
546
+ },
547
+ redundantTypeWithName: {
548
+ type: targetType,
549
+ typeName: targetTypeName,
550
+ },
551
+ });
394
552
  }
395
553
  }
396
554
  }
397
- }
398
- const overriddenTypeNodes = new Map();
399
- // For each primitive type of all the seen literal types,
400
- // if there was a primitive type seen that overrides it,
401
- // upsert the literal text and primitive type under the backing type node
402
- for (const [primitiveTypeFlag, typeNodesWithText] of seenLiteralTypes) {
403
- if (seenPrimitiveTypes.has(primitiveTypeFlag)) {
404
- for (const { literalValue, typeNode } of typeNodesWithText) {
405
- addToMapGroup(overriddenTypeNodes, typeNode, {
406
- literalValue,
407
- primitiveTypeFlag,
408
- });
555
+ for (const typePart of typeParts) {
556
+ if (typePart.type.flags === ts.TypeFlags.Any ||
557
+ typePart.type.flags === ts.TypeFlags.Unknown ||
558
+ typePart.type.flags === ts.TypeFlags.Never) {
559
+ continue;
409
560
  }
410
- }
411
- }
412
- // For each type node that had at least one overridden literal,
413
- // group those literals by their primitive type,
414
- // then report each primitive type with all its literals
415
- for (const [typeNode, typeFlagsWithText] of overriddenTypeNodes) {
416
- const grouped = (0, util_1.arrayGroupByToMap)(typeFlagsWithText, pair => pair.primitiveTypeFlag);
417
- for (const [primitiveTypeFlag, pairs] of grouped) {
418
- context.report({
419
- node: typeNode,
420
- messageId: 'literalOverridden',
421
- data: {
422
- literal: pairs.map(pair => pair.literalValue).join(' | '),
423
- primitive: primitiveTypeFlagNames[primitiveTypeFlag],
424
- },
561
+ seenTypes.add({
562
+ ...typePart,
563
+ parentTypeNode: typeNode,
425
564
  });
426
565
  }
427
566
  }
567
+ reportRedundantTypes(redundantTypes, 'union');
428
568
  },
429
569
  };
430
570
  },
@@ -5,7 +5,7 @@ type OptionTester = (type: Type, checker: TypeChecker, recursivelyCheckType: (ty
5
5
  declare const optionTesters: {
6
6
  type: "Array" | "RegExp" | "Boolean" | "Number" | "Any" | "Nullish" | "Never";
7
7
  option: "allowAny" | "allowBoolean" | "allowNullish" | "allowRegExp" | "allowNever" | "allowNumber" | "allowArray";
8
- tester: typeof isTypeAnyType | typeof isTypeNeverType | OptionTester | ((type: Type, checker: TypeChecker, recursivelyCheckType: (type: Type) => boolean) => boolean) | ((type: Type, checker: TypeChecker) => boolean);
8
+ tester: typeof isTypeAnyType | OptionTester | ((type: Type, checker: TypeChecker, recursivelyCheckType: (type: Type) => boolean) => boolean) | ((type: Type, checker: TypeChecker) => boolean) | typeof isTypeNeverType;
9
9
  }[];
10
10
  export type Options = [
11
11
  {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@typescript-eslint/eslint-plugin",
3
- "version": "8.47.1-alpha.4",
3
+ "version": "8.47.1-alpha.6",
4
4
  "description": "TypeScript plugin for ESLint",
5
5
  "files": [
6
6
  "dist",
@@ -59,10 +59,10 @@
59
59
  },
60
60
  "dependencies": {
61
61
  "@eslint-community/regexpp": "^4.10.0",
62
- "@typescript-eslint/scope-manager": "8.47.1-alpha.4",
63
- "@typescript-eslint/type-utils": "8.47.1-alpha.4",
64
- "@typescript-eslint/utils": "8.47.1-alpha.4",
65
- "@typescript-eslint/visitor-keys": "8.47.1-alpha.4",
62
+ "@typescript-eslint/scope-manager": "8.47.1-alpha.6",
63
+ "@typescript-eslint/type-utils": "8.47.1-alpha.6",
64
+ "@typescript-eslint/utils": "8.47.1-alpha.6",
65
+ "@typescript-eslint/visitor-keys": "8.47.1-alpha.6",
66
66
  "graphemer": "^1.4.0",
67
67
  "ignore": "^7.0.0",
68
68
  "natural-compare": "^1.4.0",
@@ -71,8 +71,8 @@
71
71
  "devDependencies": {
72
72
  "@types/mdast": "^4.0.3",
73
73
  "@types/natural-compare": "*",
74
- "@typescript-eslint/rule-schema-to-typescript-types": "8.47.1-alpha.4",
75
- "@typescript-eslint/rule-tester": "8.47.1-alpha.4",
74
+ "@typescript-eslint/rule-schema-to-typescript-types": "8.47.1-alpha.6",
75
+ "@typescript-eslint/rule-tester": "8.47.1-alpha.6",
76
76
  "@vitest/coverage-v8": "^3.1.3",
77
77
  "ajv": "^6.12.6",
78
78
  "cross-fetch": "*",
@@ -92,7 +92,7 @@
92
92
  "vitest": "^3.1.3"
93
93
  },
94
94
  "peerDependencies": {
95
- "@typescript-eslint/parser": "^8.47.1-alpha.4",
95
+ "@typescript-eslint/parser": "^8.47.1-alpha.6",
96
96
  "eslint": "^8.57.0 || ^9.0.0",
97
97
  "typescript": ">=4.8.4 <6.0.0"
98
98
  },