@typescript-eslint/eslint-plugin 8.48.1-alpha.9 → 8.48.2-alpha.0

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" | "overridden" | "typeOverridden", [], 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>;
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" | "overridden" | "typeOverridden", [], import("../rules").ESLintPluginDocs, TSESLint.RuleListener>;
790
+ 'no-redundant-type-constituents': TSESLint.RuleModule<"overrides" | "errorTypeOverrides" | "literalOverridden" | "overridden" | "primitiveOverridden", [], 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" | "overridden" | "typeOverridden", [], 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>;
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" | "overridden" | "typeOverridden", [], import("../../rules").ESLintPluginDocs, import("@typescript-eslint/utils/ts-eslint").RuleListener>;
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>;
2
2
  export default _default;
@@ -37,6 +37,47 @@ 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
+ ]);
40
81
  function addToMapGroup(map, key, value) {
41
82
  const existing = map.get(key);
42
83
  if (existing) {
@@ -46,270 +87,88 @@ function addToMapGroup(map, key, value) {
46
87
  map.set(key, [value]);
47
88
  }
48
89
  }
49
- function isUnionNodeInsideReturnType(node) {
50
- if (node.parent.type === utils_1.AST_NODE_TYPES.TSUnionType) {
51
- return isUnionNodeInsideReturnType(node.parent);
90
+ function describeLiteralType(type) {
91
+ if (type.isStringLiteral()) {
92
+ return JSON.stringify(type.value);
52
93
  }
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;
94
+ if ((0, util_1.isTypeBigIntLiteralType)(type)) {
95
+ return `${type.value.negative ? '-' : ''}${type.value.base10Value}n`;
62
96
  }
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
- }
97
+ if (type.isLiteral()) {
98
+ // eslint-disable-next-line @typescript-eslint/no-base-to-string
99
+ return type.value.toString();
81
100
  }
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;
101
+ if (tsutils.isIntrinsicErrorType(type) && type.aliasSymbol) {
102
+ return type.aliasSymbol.escapedName.toString();
91
103
  }
92
- if (tsutils.isUnionType(type)) {
93
- return type.types.every(typePart => shouldCheckTypeRedundancy(typePart, checker, depth));
104
+ if ((0, util_1.isTypeAnyType)(type)) {
105
+ return 'any';
94
106
  }
95
- return true;
96
- }
97
- function isTargetTypeRedundantInIntersection(sourceType, targetType, checker) {
98
- if (!shouldCheckTypeRedundancy(sourceType, checker) ||
99
- !shouldCheckTypeRedundancy(targetType, checker)) {
100
- return false;
107
+ if ((0, util_1.isTypeNeverType)(type)) {
108
+ return 'never';
101
109
  }
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);
110
+ if ((0, util_1.isTypeUnknownType)(type)) {
111
+ return 'unknown';
113
112
  }
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);
113
+ if ((0, util_1.isTypeTemplateLiteralType)(type)) {
114
+ return 'template literal type';
126
115
  }
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;
116
+ if ((0, util_1.isTypeBigIntLiteralType)(type)) {
117
+ return `${type.value.negative ? '-' : ''}${type.value.base10Value}n`;
147
118
  }
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;
119
+ if (tsutils.isTrueLiteralType(type)) {
120
+ return 'true';
174
121
  }
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;
122
+ if (tsutils.isFalseLiteralType(type)) {
123
+ return 'false';
194
124
  }
195
- return checker.isTypeAssignableTo(sourceType, targetType);
125
+ return 'literal type';
196
126
  }
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;
217
- }
218
- }
219
- return false;
220
- }
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;
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';
286
154
  }
287
- }
288
- return true;
289
155
  }
290
- return checker.isTypeAssignableTo(targetType, sourceType);
156
+ return 'literal type';
291
157
  }
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} `);
158
+ function isNodeInsideReturnType(node) {
159
+ return (node.parent.type === utils_1.AST_NODE_TYPES.TSTypeAnnotation &&
160
+ (0, util_1.isFunctionOrFunctionType)(node.parent.parent));
306
161
  }
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;
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);
313
172
  }
314
173
  exports.default = (0, util_1.createRule)({
315
174
  name: 'no-redundant-type-constituents',
@@ -322,68 +181,61 @@ exports.default = (0, util_1.createRule)({
322
181
  },
323
182
  messages: {
324
183
  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.`,
325
185
  overridden: `'{{typeName}}' is overridden by other types in this {{container}} type.`,
326
186
  overrides: `'{{typeName}}' overrides all other types in this {{container}} type.`,
327
- typeOverridden: `{{redundantType}} is overridden by {{nonRedundantType}} in this {{container}} type.`,
187
+ primitiveOverridden: `{{primitive}} is overridden by the {{literal}} in this intersection type.`,
328
188
  },
329
189
  schema: [],
330
190
  },
331
191
  defaultOptions: [],
332
192
  create(context) {
333
193
  const services = (0, util_1.getParserServices)(context);
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
- }
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
+ ];
351
204
  }
352
- }
353
- function getUnionTypePart(typeNode, checker) {
354
- if (ts.isParenthesizedTypeNode(typeNode)) {
355
- return getUnionTypePart(typeNode.type, checker);
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
+ ];
356
214
  }
357
- if (ts.isUnionTypeNode(typeNode)) {
358
- return typeNode.types.flatMap(typeNode => getUnionTypePart(typeNode, checker));
215
+ if (typeNode.type === utils_1.AST_NODE_TYPES.TSUnionType) {
216
+ return typeNode.types.flatMap(getTypeNodeTypePartFlags);
359
217
  }
360
- const type = checker.getTypeAtLocation(typeNode);
361
- return [
362
- {
363
- type,
364
- typeName: checker.typeToString(type),
365
- },
366
- ];
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
+ }));
367
224
  }
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));
225
+ function getTypeNodeTypePartFlagsCached(typeNode) {
226
+ const existing = typesCache.get(typeNode);
227
+ if (existing) {
228
+ return existing;
374
229
  }
375
- const type = checker.getTypeAtLocation(typeNode);
376
- return [
377
- {
378
- type,
379
- typeName: checker.typeToString(type),
380
- },
381
- ];
230
+ const created = getTypeNodeTypePartFlags(typeNode);
231
+ typesCache.set(typeNode, created);
232
+ return created;
382
233
  }
383
234
  return {
384
- TSIntersectionType(node) {
385
- const seenTypes = new Set();
386
- const redundantTypes = new Map();
235
+ 'TSIntersectionType:exit'(node) {
236
+ const seenLiteralTypes = new Map();
237
+ const seenPrimitiveTypes = new Map();
238
+ const seenUnionTypes = new Map();
387
239
  function checkIntersectionBottomAndTopTypes({ typeFlags, typeName }, typeNode) {
388
240
  for (const [messageId, checkFlag] of [
389
241
  ['overrides', ts.TypeFlags.Any],
@@ -407,70 +259,87 @@ exports.default = (0, util_1.createRule)({
407
259
  return false;
408
260
  }
409
261
  for (const typeNode of node.types) {
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);
262
+ const typePartFlags = getTypeNodeTypePartFlagsCached(typeNode);
263
+ for (const typePart of typePartFlags) {
264
+ if (checkIntersectionBottomAndTopTypes(typePart, typeNode)) {
420
265
  continue;
421
266
  }
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;
267
+ for (const literalTypeFlag of literalTypeFlags) {
268
+ if (typePart.typeFlags === literalTypeFlag) {
269
+ addToMapGroup(seenLiteralTypes, literalToPrimitiveTypeFlags[literalTypeFlag], typePart.typeName);
270
+ break;
430
271
  }
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
- });
272
+ }
273
+ for (const primitiveTypeFlag of primitiveTypeFlags) {
274
+ if (typePart.typeFlags === primitiveTypeFlag) {
275
+ addToMapGroup(seenPrimitiveTypes, primitiveTypeFlag, typeNode);
442
276
  }
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
- });
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];
299
+ }
300
+ else {
301
+ primitive = undefined;
302
+ break;
454
303
  }
455
304
  }
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
+ }
456
315
  }
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;
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
+ });
462
336
  }
463
- seenTypes.add({
464
- ...typePart,
465
- parentTypeNode: typeNode,
466
- });
467
337
  }
468
338
  }
469
- reportRedundantTypes(redundantTypes, 'intersection');
470
339
  },
471
- TSUnionType(node) {
472
- const seenTypes = new Set();
473
- const redundantTypes = new Map();
340
+ 'TSUnionType:exit'(node) {
341
+ const seenLiteralTypes = new Map();
342
+ const seenPrimitiveTypes = new Set();
474
343
  function checkUnionBottomAndTopTypes({ typeFlags, typeName }, typeNode) {
475
344
  for (const checkFlag of [
476
345
  ts.TypeFlags.Any,
@@ -491,7 +360,7 @@ exports.default = (0, util_1.createRule)({
491
360
  }
492
361
  }
493
362
  if (typeFlags === ts.TypeFlags.Never &&
494
- !isUnionNodeInsideReturnType(node)) {
363
+ !isNodeInsideReturnType(node)) {
495
364
  context.report({
496
365
  node: typeNode,
497
366
  messageId: 'overridden',
@@ -505,66 +374,57 @@ exports.default = (0, util_1.createRule)({
505
374
  return false;
506
375
  }
507
376
  for (const typeNode of node.types) {
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);
377
+ const typePartFlags = getTypeNodeTypePartFlagsCached(typeNode);
378
+ for (const typePart of typePartFlags) {
379
+ if (checkUnionBottomAndTopTypes(typePart, typeNode)) {
518
380
  continue;
519
381
  }
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
- },
382
+ for (const literalTypeFlag of literalTypeFlags) {
383
+ if (typePart.typeFlags === literalTypeFlag) {
384
+ addToMapGroup(seenLiteralTypes, literalToPrimitiveTypeFlags[literalTypeFlag], {
385
+ literalValue: typePart.typeName,
386
+ typeNode,
539
387
  });
388
+ break;
540
389
  }
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
- });
390
+ }
391
+ for (const primitiveTypeFlag of primitiveTypeFlags) {
392
+ if ((typePart.typeFlags & primitiveTypeFlag) !== 0) {
393
+ seenPrimitiveTypes.add(primitiveTypeFlag);
552
394
  }
553
395
  }
554
396
  }
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;
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
+ });
560
409
  }
561
- seenTypes.add({
562
- ...typePart,
563
- parentTypeNode: typeNode,
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
+ },
564
425
  });
565
426
  }
566
427
  }
567
- reportRedundantTypes(redundantTypes, 'union');
568
428
  },
569
429
  };
570
430
  },
@@ -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 | OptionTester | ((type: Type, checker: TypeChecker, recursivelyCheckType: (type: Type) => boolean) => boolean) | ((type: Type, checker: TypeChecker) => boolean) | typeof isTypeNeverType;
8
+ tester: typeof isTypeAnyType | typeof isTypeNeverType | OptionTester | ((type: Type, checker: TypeChecker, recursivelyCheckType: (type: Type) => boolean) => boolean) | ((type: Type, checker: TypeChecker) => boolean);
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.48.1-alpha.9",
3
+ "version": "8.48.2-alpha.0",
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.48.1-alpha.9",
63
- "@typescript-eslint/type-utils": "8.48.1-alpha.9",
64
- "@typescript-eslint/utils": "8.48.1-alpha.9",
65
- "@typescript-eslint/visitor-keys": "8.48.1-alpha.9",
62
+ "@typescript-eslint/scope-manager": "8.48.2-alpha.0",
63
+ "@typescript-eslint/type-utils": "8.48.2-alpha.0",
64
+ "@typescript-eslint/utils": "8.48.2-alpha.0",
65
+ "@typescript-eslint/visitor-keys": "8.48.2-alpha.0",
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.48.1-alpha.9",
75
- "@typescript-eslint/rule-tester": "8.48.1-alpha.9",
74
+ "@typescript-eslint/rule-schema-to-typescript-types": "8.48.2-alpha.0",
75
+ "@typescript-eslint/rule-tester": "8.48.2-alpha.0",
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.48.1-alpha.9",
95
+ "@typescript-eslint/parser": "^8.48.2-alpha.0",
96
96
  "eslint": "^8.57.0 || ^9.0.0",
97
97
  "typescript": ">=4.8.4 <6.0.0"
98
98
  },