@typescript-eslint/eslint-plugin 8.58.3-alpha.4 → 8.58.3-alpha.5
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.
|
@@ -330,7 +330,8 @@ exports.default = (0, util_1.createRule)({
|
|
|
330
330
|
const objectType = services.getTypeAtLocation(node.object);
|
|
331
331
|
const propertyName = propertyType.isStringLiteral()
|
|
332
332
|
? propertyType.value
|
|
333
|
-
:
|
|
333
|
+
: // eslint-disable-next-line @typescript-eslint/no-base-to-string
|
|
334
|
+
String(propertyType.value);
|
|
334
335
|
const property = objectType.getProperty(propertyName);
|
|
335
336
|
const reason = getJsDocDeprecation(property);
|
|
336
337
|
if (reason == null) {
|
|
@@ -163,35 +163,350 @@ exports.default = (0, util_1.createRule)({
|
|
|
163
163
|
maybeDeclarationNode.kind === 'const') ||
|
|
164
164
|
(parent.type === utils_1.AST_NODE_TYPES.PropertyDefinition && parent.readonly));
|
|
165
165
|
}
|
|
166
|
-
function isTypeUnchanged(uncast, cast) {
|
|
166
|
+
function isTypeUnchanged(node, expression, uncast, cast) {
|
|
167
167
|
if (uncast === cast) {
|
|
168
168
|
return true;
|
|
169
169
|
}
|
|
170
|
+
if (node.typeAnnotation.type === utils_1.AST_NODE_TYPES.TSIntersectionType &&
|
|
171
|
+
containsTypeVariable(cast)) {
|
|
172
|
+
return false;
|
|
173
|
+
}
|
|
170
174
|
if ((0, util_1.isTypeFlagSet)(uncast, ts.TypeFlags.Undefined) &&
|
|
171
175
|
(0, util_1.isTypeFlagSet)(cast, ts.TypeFlags.Undefined) &&
|
|
172
176
|
tsutils.isCompilerOptionEnabled(compilerOptions, 'exactOptionalPropertyTypes')) {
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
177
|
+
return areUnionPartsEquivalentIgnoringUndefined(uncast, cast);
|
|
178
|
+
}
|
|
179
|
+
if (((0, util_1.isTypeFlagSet)(uncast, ts.TypeFlags.NonPrimitive) &&
|
|
180
|
+
!(0, util_1.isTypeFlagSet)(cast, ts.TypeFlags.NonPrimitive)) ||
|
|
181
|
+
(hasIndexSignature(uncast) && !hasIndexSignature(cast)) ||
|
|
182
|
+
containsAny(uncast) ||
|
|
183
|
+
containsAny(cast) ||
|
|
184
|
+
(containsTypeVariable(cast) && !containsTypeVariable(uncast))) {
|
|
185
|
+
return false;
|
|
186
|
+
}
|
|
187
|
+
if (isConceptuallyLiteral(expression) &&
|
|
188
|
+
(expression.type !== utils_1.AST_NODE_TYPES.ObjectExpression ||
|
|
189
|
+
expression.properties.length === 0 ||
|
|
190
|
+
cast
|
|
191
|
+
.getProperties()
|
|
192
|
+
.some(p => isTypeLiteral(checker.getTypeOfSymbol(p))))) {
|
|
193
|
+
return false;
|
|
194
|
+
}
|
|
195
|
+
if (cast.isIntersection() && !uncast.isIntersection()) {
|
|
196
|
+
const castParts = cast.types;
|
|
197
|
+
const otherPart = castParts.find(part => part !== uncast);
|
|
198
|
+
if (tsutils.isTypeParameter(uncast) &&
|
|
199
|
+
castParts.length === 2 &&
|
|
200
|
+
castParts.some(part => part === uncast) &&
|
|
201
|
+
otherPart != null &&
|
|
202
|
+
isEmptyObjectType(otherPart) &&
|
|
203
|
+
!containsTypeVariable(otherPart)) {
|
|
204
|
+
const constraint = checker.getBaseConstraintOfType(uncast);
|
|
205
|
+
if (constraint && !(0, util_1.isNullableType)(constraint)) {
|
|
206
|
+
return true;
|
|
207
|
+
}
|
|
181
208
|
}
|
|
182
|
-
|
|
183
|
-
return castParts.every(part => uncastPartsSet.has(part));
|
|
209
|
+
return false;
|
|
184
210
|
}
|
|
185
|
-
|
|
211
|
+
if (!hasSameProperties(uncast, cast) ||
|
|
212
|
+
!haveSameTypeArguments(uncast, cast)) {
|
|
213
|
+
return false;
|
|
214
|
+
}
|
|
215
|
+
return areMutuallyAssignable(uncast, cast);
|
|
186
216
|
}
|
|
187
217
|
function isTypeLiteral(type) {
|
|
188
218
|
return type.isLiteral() || tsutils.isBooleanLiteralType(type);
|
|
189
219
|
}
|
|
220
|
+
function hasIndexSignature(type) {
|
|
221
|
+
return checker.getIndexInfosOfType(type).length > 0;
|
|
222
|
+
}
|
|
223
|
+
function typeContains(type, predicate, seen = new Set()) {
|
|
224
|
+
if (seen.has(type)) {
|
|
225
|
+
return false;
|
|
226
|
+
}
|
|
227
|
+
seen.add(type);
|
|
228
|
+
if (predicate(type)) {
|
|
229
|
+
return true;
|
|
230
|
+
}
|
|
231
|
+
if (type.isUnionOrIntersection()) {
|
|
232
|
+
return type.types.some(t => typeContains(t, predicate, seen));
|
|
233
|
+
}
|
|
234
|
+
const nestedTypes = [
|
|
235
|
+
...checker.getTypeArguments(type),
|
|
236
|
+
...type
|
|
237
|
+
.getCallSignatures()
|
|
238
|
+
.flatMap(sig => [
|
|
239
|
+
sig.getReturnType(),
|
|
240
|
+
...sig.getParameters().map(p => checker.getTypeOfSymbol(p)),
|
|
241
|
+
]),
|
|
242
|
+
];
|
|
243
|
+
return nestedTypes.some(t => typeContains(t, predicate, seen));
|
|
244
|
+
}
|
|
245
|
+
function containsAny(type) {
|
|
246
|
+
return typeContains(type, t => (0, util_1.isTypeFlagSet)(t, ts.TypeFlags.Any));
|
|
247
|
+
}
|
|
248
|
+
function containsTypeVariable(type) {
|
|
249
|
+
return typeContains(type, t => (0, util_1.isTypeFlagSet)(t, ts.TypeFlags.TypeVariable | ts.TypeFlags.Index));
|
|
250
|
+
}
|
|
251
|
+
function hasTypeParams(sig) {
|
|
252
|
+
return (sig.getTypeParameters()?.length ?? 0) > 0;
|
|
253
|
+
}
|
|
254
|
+
function genericsMismatch(uncast, contextual) {
|
|
255
|
+
return contextual.getProperties().some(prop => {
|
|
256
|
+
const contextualSigs = checker.getSignaturesOfType(checker.getTypeOfSymbol(prop), ts.SignatureKind.Call);
|
|
257
|
+
if (!contextualSigs.some(hasTypeParams)) {
|
|
258
|
+
return false;
|
|
259
|
+
}
|
|
260
|
+
const uncastProp = uncast.getProperty(prop.getEscapedName());
|
|
261
|
+
if (!uncastProp) {
|
|
262
|
+
return true;
|
|
263
|
+
}
|
|
264
|
+
return !checker
|
|
265
|
+
.getSignaturesOfType(checker.getTypeOfSymbol(uncastProp), ts.SignatureKind.Call)
|
|
266
|
+
.some(hasTypeParams);
|
|
267
|
+
});
|
|
268
|
+
}
|
|
269
|
+
function hasSameProperties(uncast, cast) {
|
|
270
|
+
const uncastProps = uncast.getProperties();
|
|
271
|
+
const castProps = cast.getProperties();
|
|
272
|
+
if (uncastProps.length !== castProps.length) {
|
|
273
|
+
return false;
|
|
274
|
+
}
|
|
275
|
+
const castPropNames = new Set(castProps.map(p => p.getEscapedName()));
|
|
276
|
+
return uncastProps.every(prop => {
|
|
277
|
+
const name = prop.getEscapedName();
|
|
278
|
+
return (castPropNames.has(name) &&
|
|
279
|
+
tsutils.isPropertyReadonlyInType(uncast, name, checker) ===
|
|
280
|
+
tsutils.isPropertyReadonlyInType(cast, name, checker));
|
|
281
|
+
});
|
|
282
|
+
}
|
|
283
|
+
function haveSameTypeArguments(uncast, cast) {
|
|
284
|
+
const uncastArgs = checker.getTypeArguments(uncast);
|
|
285
|
+
const castArgs = checker.getTypeArguments(cast);
|
|
286
|
+
return (uncastArgs.length === castArgs.length &&
|
|
287
|
+
uncastArgs.every((arg, i) => arg === castArgs[i]));
|
|
288
|
+
}
|
|
289
|
+
function areMutuallyAssignable(a, b) {
|
|
290
|
+
return (checker.isTypeAssignableTo(a, b) && checker.isTypeAssignableTo(b, a));
|
|
291
|
+
}
|
|
292
|
+
function areUnionPartsEquivalentIgnoringUndefined(uncast, cast) {
|
|
293
|
+
const filterUndefined = (part) => !(0, util_1.isTypeFlagSet)(part, ts.TypeFlags.Undefined);
|
|
294
|
+
const uncastParts = tsutils
|
|
295
|
+
.unionConstituents(uncast)
|
|
296
|
+
.filter(filterUndefined);
|
|
297
|
+
const castParts = tsutils.unionConstituents(cast).filter(filterUndefined);
|
|
298
|
+
if (uncastParts.length !== castParts.length) {
|
|
299
|
+
return false;
|
|
300
|
+
}
|
|
301
|
+
const uncastPartsSet = new Set(uncastParts);
|
|
302
|
+
return castParts.every(part => uncastPartsSet.has(part));
|
|
303
|
+
}
|
|
304
|
+
function getOriginalExpression(node) {
|
|
305
|
+
let current = node.expression;
|
|
306
|
+
while (current.type === utils_1.AST_NODE_TYPES.TSAsExpression ||
|
|
307
|
+
current.type === utils_1.AST_NODE_TYPES.TSTypeAssertion) {
|
|
308
|
+
current = current.expression;
|
|
309
|
+
}
|
|
310
|
+
return current;
|
|
311
|
+
}
|
|
312
|
+
function isDoubleAssertionUnnecessary(node, contextualType) {
|
|
313
|
+
const innerExpression = node.expression;
|
|
314
|
+
if (innerExpression.type !== utils_1.AST_NODE_TYPES.TSAsExpression &&
|
|
315
|
+
innerExpression.type !== utils_1.AST_NODE_TYPES.TSTypeAssertion) {
|
|
316
|
+
return false;
|
|
317
|
+
}
|
|
318
|
+
const originalExpr = getOriginalExpression(node);
|
|
319
|
+
const originalType = services.getTypeAtLocation(originalExpr);
|
|
320
|
+
const castType = services.getTypeAtLocation(node);
|
|
321
|
+
if (isTypeUnchanged(node, innerExpression, originalType, castType) &&
|
|
322
|
+
!(0, util_1.isTypeFlagSet)(castType, ts.TypeFlags.Any)) {
|
|
323
|
+
return 'unnecessaryAssertion';
|
|
324
|
+
}
|
|
325
|
+
if (contextualType) {
|
|
326
|
+
const intermediateType = services.getTypeAtLocation(innerExpression);
|
|
327
|
+
if (((0, util_1.isTypeFlagSet)(intermediateType, ts.TypeFlags.Any) ||
|
|
328
|
+
(0, util_1.isTypeFlagSet)(intermediateType, ts.TypeFlags.Unknown)) &&
|
|
329
|
+
checker.isTypeAssignableTo(originalType, contextualType)) {
|
|
330
|
+
return 'contextuallyUnnecessary';
|
|
331
|
+
}
|
|
332
|
+
}
|
|
333
|
+
return false;
|
|
334
|
+
}
|
|
335
|
+
const CONCEPTUALLY_LITERAL_TYPES = new Set([
|
|
336
|
+
utils_1.AST_NODE_TYPES.Literal,
|
|
337
|
+
utils_1.AST_NODE_TYPES.ArrayExpression,
|
|
338
|
+
utils_1.AST_NODE_TYPES.ObjectExpression,
|
|
339
|
+
utils_1.AST_NODE_TYPES.TemplateLiteral,
|
|
340
|
+
utils_1.AST_NODE_TYPES.ClassExpression,
|
|
341
|
+
utils_1.AST_NODE_TYPES.FunctionExpression,
|
|
342
|
+
utils_1.AST_NODE_TYPES.ArrowFunctionExpression,
|
|
343
|
+
utils_1.AST_NODE_TYPES.JSXElement,
|
|
344
|
+
utils_1.AST_NODE_TYPES.JSXFragment,
|
|
345
|
+
]);
|
|
346
|
+
function isConceptuallyLiteral(node) {
|
|
347
|
+
return CONCEPTUALLY_LITERAL_TYPES.has(node.type);
|
|
348
|
+
}
|
|
190
349
|
function isIIFE(expression) {
|
|
191
350
|
return (expression.type === utils_1.AST_NODE_TYPES.CallExpression &&
|
|
192
351
|
(expression.callee.type === utils_1.AST_NODE_TYPES.ArrowFunctionExpression ||
|
|
193
352
|
expression.callee.type === utils_1.AST_NODE_TYPES.FunctionExpression));
|
|
194
353
|
}
|
|
354
|
+
function isEmptyObjectType(type) {
|
|
355
|
+
return ((0, util_1.isTypeFlagSet)(type, ts.TypeFlags.NonPrimitive) ||
|
|
356
|
+
(type.getProperties().length === 0 &&
|
|
357
|
+
!type.getCallSignatures().length &&
|
|
358
|
+
!type.getConstructSignatures().length &&
|
|
359
|
+
!type.getStringIndexType() &&
|
|
360
|
+
!type.getNumberIndexType()));
|
|
361
|
+
}
|
|
362
|
+
function hasGenericCallSignature(type) {
|
|
363
|
+
return type.getCallSignatures().some(hasTypeParams);
|
|
364
|
+
}
|
|
365
|
+
function isArgumentToOverloadedFunction(node) {
|
|
366
|
+
const { parent } = node;
|
|
367
|
+
if ((parent.type !== utils_1.AST_NODE_TYPES.CallExpression &&
|
|
368
|
+
parent.type !== utils_1.AST_NODE_TYPES.NewExpression) ||
|
|
369
|
+
!parent.arguments.includes(node)) {
|
|
370
|
+
return false;
|
|
371
|
+
}
|
|
372
|
+
const calleeType = checker.getTypeAtLocation(services.esTreeNodeToTSNodeMap.get(parent.callee));
|
|
373
|
+
const signatures = calleeType.getCallSignatures();
|
|
374
|
+
if (signatures.length <= 1) {
|
|
375
|
+
return false;
|
|
376
|
+
}
|
|
377
|
+
const argIndex = parent.arguments.indexOf(node);
|
|
378
|
+
const paramTypes = signatures.map(sig => {
|
|
379
|
+
const params = sig.getParameters();
|
|
380
|
+
if (argIndex >= params.length) {
|
|
381
|
+
return undefined;
|
|
382
|
+
}
|
|
383
|
+
const param = params[argIndex];
|
|
384
|
+
let paramType = checker.getTypeOfSymbol(param);
|
|
385
|
+
if (param.valueDeclaration &&
|
|
386
|
+
ts.isParameter(param.valueDeclaration) &&
|
|
387
|
+
param.valueDeclaration.dotDotDotToken) {
|
|
388
|
+
const typeArgs = checker.getTypeArguments(paramType);
|
|
389
|
+
if (typeArgs.length > 0) {
|
|
390
|
+
paramType = typeArgs[0];
|
|
391
|
+
}
|
|
392
|
+
}
|
|
393
|
+
return paramType;
|
|
394
|
+
});
|
|
395
|
+
if (paramTypes.some(type => type == null)) {
|
|
396
|
+
return true;
|
|
397
|
+
}
|
|
398
|
+
const definedParamTypes = paramTypes;
|
|
399
|
+
const firstParamType = definedParamTypes[0];
|
|
400
|
+
if (definedParamTypes.every(type => type === firstParamType)) {
|
|
401
|
+
return false;
|
|
402
|
+
}
|
|
403
|
+
const uncastType = services.getTypeAtLocation(node.expression);
|
|
404
|
+
return !definedParamTypes.every(type => checker.isTypeAssignableTo(uncastType, type));
|
|
405
|
+
}
|
|
406
|
+
function isInDestructuringDeclaration(node) {
|
|
407
|
+
const { parent } = node;
|
|
408
|
+
return (parent.type === utils_1.AST_NODE_TYPES.VariableDeclarator &&
|
|
409
|
+
parent.init === node &&
|
|
410
|
+
(parent.id.type === utils_1.AST_NODE_TYPES.ObjectPattern ||
|
|
411
|
+
parent.id.type === utils_1.AST_NODE_TYPES.ArrayPattern));
|
|
412
|
+
}
|
|
413
|
+
function isPropertyInProblematicContext(node) {
|
|
414
|
+
const { parent } = node;
|
|
415
|
+
if (parent.type !== utils_1.AST_NODE_TYPES.Property || parent.value !== node) {
|
|
416
|
+
return false;
|
|
417
|
+
}
|
|
418
|
+
const objectExpr = parent.parent;
|
|
419
|
+
if (objectExpr.type !== utils_1.AST_NODE_TYPES.ObjectExpression) {
|
|
420
|
+
return false;
|
|
421
|
+
}
|
|
422
|
+
const objectTsNode = services.esTreeNodeToTSNodeMap.get(objectExpr);
|
|
423
|
+
if (checker.getContextualType(objectTsNode)?.isUnion()) {
|
|
424
|
+
const nodeTsNode = services.esTreeNodeToTSNodeMap.get(node);
|
|
425
|
+
const propContextualType = checker.getContextualType(nodeTsNode);
|
|
426
|
+
if (propContextualType == null) {
|
|
427
|
+
return true;
|
|
428
|
+
}
|
|
429
|
+
const nonNullableContextualType = checker.getNonNullableType(propContextualType);
|
|
430
|
+
if (nonNullableContextualType.isUnion()) {
|
|
431
|
+
return true;
|
|
432
|
+
}
|
|
433
|
+
const uncastType = services.getTypeAtLocation(node.expression);
|
|
434
|
+
return !checker.isTypeAssignableTo(uncastType, nonNullableContextualType);
|
|
435
|
+
}
|
|
436
|
+
const objectParent = objectExpr.parent;
|
|
437
|
+
return (objectParent.type === utils_1.AST_NODE_TYPES.TSSatisfiesExpression ||
|
|
438
|
+
(objectParent.type === utils_1.AST_NODE_TYPES.CallExpression &&
|
|
439
|
+
objectParent.parent.type === utils_1.AST_NODE_TYPES.TSSatisfiesExpression));
|
|
440
|
+
}
|
|
441
|
+
function isAssignmentInNonStatementContext(node) {
|
|
442
|
+
const { parent } = node;
|
|
443
|
+
if (parent.type !== utils_1.AST_NODE_TYPES.AssignmentExpression ||
|
|
444
|
+
parent.right !== node) {
|
|
445
|
+
return false;
|
|
446
|
+
}
|
|
447
|
+
const assignmentParent = parent.parent;
|
|
448
|
+
return assignmentParent.type !== utils_1.AST_NODE_TYPES.ExpressionStatement;
|
|
449
|
+
}
|
|
450
|
+
function isInGenericContext(node) {
|
|
451
|
+
let seenFunction = false;
|
|
452
|
+
for (let current = node.parent; current; current = current.parent) {
|
|
453
|
+
if (current.type === utils_1.AST_NODE_TYPES.FunctionDeclaration) {
|
|
454
|
+
return false;
|
|
455
|
+
}
|
|
456
|
+
if (current.type === utils_1.AST_NODE_TYPES.FunctionExpression ||
|
|
457
|
+
current.type === utils_1.AST_NODE_TYPES.ArrowFunctionExpression) {
|
|
458
|
+
if (current.body.type === utils_1.AST_NODE_TYPES.BlockStatement) {
|
|
459
|
+
return false;
|
|
460
|
+
}
|
|
461
|
+
if (seenFunction) {
|
|
462
|
+
return false;
|
|
463
|
+
}
|
|
464
|
+
seenFunction = true;
|
|
465
|
+
}
|
|
466
|
+
if (current.type === utils_1.AST_NODE_TYPES.CallExpression ||
|
|
467
|
+
current.type === utils_1.AST_NODE_TYPES.NewExpression) {
|
|
468
|
+
if (current.typeArguments != null) {
|
|
469
|
+
continue;
|
|
470
|
+
}
|
|
471
|
+
if (current.type === utils_1.AST_NODE_TYPES.CallExpression &&
|
|
472
|
+
current.callee.type === utils_1.AST_NODE_TYPES.MemberExpression &&
|
|
473
|
+
current.arguments.includes(node)) {
|
|
474
|
+
continue;
|
|
475
|
+
}
|
|
476
|
+
const calleeType = checker.getTypeAtLocation(services.esTreeNodeToTSNodeMap.get(current.callee));
|
|
477
|
+
if (hasGenericCallSignature(calleeType)) {
|
|
478
|
+
return true;
|
|
479
|
+
}
|
|
480
|
+
}
|
|
481
|
+
}
|
|
482
|
+
return false;
|
|
483
|
+
}
|
|
484
|
+
const SKIP_PARENT_TYPES = new Set([
|
|
485
|
+
utils_1.AST_NODE_TYPES.TSAsExpression,
|
|
486
|
+
utils_1.AST_NODE_TYPES.TSTypeAssertion,
|
|
487
|
+
utils_1.AST_NODE_TYPES.SpreadElement,
|
|
488
|
+
utils_1.AST_NODE_TYPES.TSSatisfiesExpression,
|
|
489
|
+
]);
|
|
490
|
+
function shouldSkipContextualTypeFallback(node, castIsAny) {
|
|
491
|
+
if (castIsAny) {
|
|
492
|
+
return (node.parent.type === utils_1.AST_NODE_TYPES.LogicalExpression ||
|
|
493
|
+
isInGenericContext(node));
|
|
494
|
+
}
|
|
495
|
+
if (SKIP_PARENT_TYPES.has(node.parent.type) ||
|
|
496
|
+
node.expression.type === utils_1.AST_NODE_TYPES.ArrayExpression ||
|
|
497
|
+
isInDestructuringDeclaration(node) ||
|
|
498
|
+
isPropertyInProblematicContext(node) ||
|
|
499
|
+
isAssignmentInNonStatementContext(node) ||
|
|
500
|
+
isArgumentToOverloadedFunction(node)) {
|
|
501
|
+
return true;
|
|
502
|
+
}
|
|
503
|
+
if (isInGenericContext(node)) {
|
|
504
|
+
const originalExpr = getOriginalExpression(node);
|
|
505
|
+
return (!isConceptuallyLiteral(originalExpr) &&
|
|
506
|
+
node.parent.type !== utils_1.AST_NODE_TYPES.Property);
|
|
507
|
+
}
|
|
508
|
+
return false;
|
|
509
|
+
}
|
|
195
510
|
function getUncastType(node) {
|
|
196
511
|
// Special handling for IIFE: extract the function's return type
|
|
197
512
|
if (isIIFE(node.expression)) {
|
|
@@ -211,6 +526,44 @@ exports.default = (0, util_1.createRule)({
|
|
|
211
526
|
}
|
|
212
527
|
return services.getTypeAtLocation(node.expression);
|
|
213
528
|
}
|
|
529
|
+
function createAssertionFixer(node) {
|
|
530
|
+
return fixer => {
|
|
531
|
+
if (node.type === utils_1.AST_NODE_TYPES.TSTypeAssertion) {
|
|
532
|
+
const openingAngleBracket = (0, util_1.nullThrows)(context.sourceCode.getTokenBefore(node.typeAnnotation, token => token.type === utils_1.AST_TOKEN_TYPES.Punctuator &&
|
|
533
|
+
token.value === '<'), util_1.NullThrowsReasons.MissingToken('<', 'type annotation'));
|
|
534
|
+
const closingAngleBracket = (0, util_1.nullThrows)(context.sourceCode.getTokenAfter(node.typeAnnotation, token => token.type === utils_1.AST_TOKEN_TYPES.Punctuator &&
|
|
535
|
+
token.value === '>'), util_1.NullThrowsReasons.MissingToken('>', 'type annotation'));
|
|
536
|
+
return fixer.removeRange([
|
|
537
|
+
openingAngleBracket.range[0],
|
|
538
|
+
closingAngleBracket.range[1],
|
|
539
|
+
]);
|
|
540
|
+
}
|
|
541
|
+
const asToken = (0, util_1.nullThrows)(context.sourceCode.getTokenAfter(node.expression, token => token.type === utils_1.AST_TOKEN_TYPES.Identifier && token.value === 'as'), util_1.NullThrowsReasons.MissingToken('>', 'type annotation'));
|
|
542
|
+
const tokenBeforeAs = (0, util_1.nullThrows)(context.sourceCode.getTokenBefore(asToken, {
|
|
543
|
+
includeComments: true,
|
|
544
|
+
}), util_1.NullThrowsReasons.MissingToken('comment', 'as'));
|
|
545
|
+
return fixer.removeRange([tokenBeforeAs.range[1], node.range[1]]);
|
|
546
|
+
};
|
|
547
|
+
}
|
|
548
|
+
function reportDoubleAssertionIfUnnecessary(node, contextualType) {
|
|
549
|
+
const doubleAssertionResult = isDoubleAssertionUnnecessary(node, contextualType);
|
|
550
|
+
if (doubleAssertionResult) {
|
|
551
|
+
context.report({
|
|
552
|
+
node,
|
|
553
|
+
messageId: doubleAssertionResult,
|
|
554
|
+
fix(fixer) {
|
|
555
|
+
const originalExpr = getOriginalExpression(node);
|
|
556
|
+
let text = context.sourceCode.getText(originalExpr);
|
|
557
|
+
if (originalExpr.type === utils_1.AST_NODE_TYPES.ObjectExpression &&
|
|
558
|
+
node.parent.type === utils_1.AST_NODE_TYPES.ArrowFunctionExpression &&
|
|
559
|
+
node.parent.body === node) {
|
|
560
|
+
text = `(${text})`;
|
|
561
|
+
}
|
|
562
|
+
return fixer.replaceText(node, text);
|
|
563
|
+
},
|
|
564
|
+
});
|
|
565
|
+
}
|
|
566
|
+
}
|
|
214
567
|
return {
|
|
215
568
|
'TSAsExpression, TSTypeAssertion'(node) {
|
|
216
569
|
if (options.typesToIgnore?.includes(context.sourceCode.getText(node.typeAnnotation))) {
|
|
@@ -225,7 +578,7 @@ exports.default = (0, util_1.createRule)({
|
|
|
225
578
|
return;
|
|
226
579
|
}
|
|
227
580
|
const uncastType = getUncastType(node);
|
|
228
|
-
const typeIsUnchanged = isTypeUnchanged(uncastType, castType);
|
|
581
|
+
const typeIsUnchanged = isTypeUnchanged(node, node.expression, uncastType, castType);
|
|
229
582
|
const wouldSameTypeBeInferred = castTypeIsLiteral
|
|
230
583
|
? isImplicitlyNarrowedLiteralDeclaration(node)
|
|
231
584
|
: !typeAnnotationIsConstAssertion;
|
|
@@ -233,32 +586,46 @@ exports.default = (0, util_1.createRule)({
|
|
|
233
586
|
context.report({
|
|
234
587
|
node,
|
|
235
588
|
messageId: 'unnecessaryAssertion',
|
|
236
|
-
fix(
|
|
237
|
-
if (node.type === utils_1.AST_NODE_TYPES.TSTypeAssertion) {
|
|
238
|
-
const openingAngleBracket = (0, util_1.nullThrows)(context.sourceCode.getTokenBefore(node.typeAnnotation, token => token.type === utils_1.AST_TOKEN_TYPES.Punctuator &&
|
|
239
|
-
token.value === '<'), util_1.NullThrowsReasons.MissingToken('<', 'type annotation'));
|
|
240
|
-
const closingAngleBracket = (0, util_1.nullThrows)(context.sourceCode.getTokenAfter(node.typeAnnotation, token => token.type === utils_1.AST_TOKEN_TYPES.Punctuator &&
|
|
241
|
-
token.value === '>'), util_1.NullThrowsReasons.MissingToken('>', 'type annotation'));
|
|
242
|
-
// < ( number ) > ( 3 + 5 )
|
|
243
|
-
// ^---remove---^
|
|
244
|
-
return fixer.removeRange([
|
|
245
|
-
openingAngleBracket.range[0],
|
|
246
|
-
closingAngleBracket.range[1],
|
|
247
|
-
]);
|
|
248
|
-
}
|
|
249
|
-
// `as` is always present in TSAsExpression
|
|
250
|
-
const asToken = (0, util_1.nullThrows)(context.sourceCode.getTokenAfter(node.expression, token => token.type === utils_1.AST_TOKEN_TYPES.Identifier &&
|
|
251
|
-
token.value === 'as'), util_1.NullThrowsReasons.MissingToken('>', 'type annotation'));
|
|
252
|
-
const tokenBeforeAs = (0, util_1.nullThrows)(context.sourceCode.getTokenBefore(asToken, {
|
|
253
|
-
includeComments: true,
|
|
254
|
-
}), util_1.NullThrowsReasons.MissingToken('comment', 'as'));
|
|
255
|
-
// ( 3 + 5 ) as number
|
|
256
|
-
// ^--remove--^
|
|
257
|
-
return fixer.removeRange([tokenBeforeAs.range[1], node.range[1]]);
|
|
258
|
-
},
|
|
589
|
+
fix: createAssertionFixer(node),
|
|
259
590
|
});
|
|
591
|
+
return;
|
|
592
|
+
}
|
|
593
|
+
const originalNode = services.esTreeNodeToTSNodeMap.get(node);
|
|
594
|
+
const castIsAny = (0, util_1.isTypeFlagSet)(castType, ts.TypeFlags.Any) &&
|
|
595
|
+
!SKIP_PARENT_TYPES.has(node.parent.type);
|
|
596
|
+
const contextualType = shouldSkipContextualTypeFallback(node, castIsAny)
|
|
597
|
+
? undefined
|
|
598
|
+
: checker.getContextualType(originalNode);
|
|
599
|
+
if (contextualType) {
|
|
600
|
+
const contextualTypeIsAny = (0, util_1.isTypeFlagSet)(contextualType, ts.TypeFlags.Any);
|
|
601
|
+
const isCallArgument = (node.parent.type === utils_1.AST_NODE_TYPES.CallExpression ||
|
|
602
|
+
node.parent.type === utils_1.AST_NODE_TYPES.NewExpression) &&
|
|
603
|
+
node.parent.arguments.includes(node);
|
|
604
|
+
const anyInvolvedInContextualCheck = contextualTypeIsAny
|
|
605
|
+
? isCallArgument && !containsAny(castType)
|
|
606
|
+
: !containsAny(contextualType);
|
|
607
|
+
const isNullishLiteralToUnion = castType.isUnion() &&
|
|
608
|
+
((node.expression.type === utils_1.AST_NODE_TYPES.Literal &&
|
|
609
|
+
node.expression.value == null) ||
|
|
610
|
+
(node.expression.type === utils_1.AST_NODE_TYPES.Identifier &&
|
|
611
|
+
node.expression.name === 'undefined'));
|
|
612
|
+
const isContextuallyUnnecessary = !typeAnnotationIsConstAssertion &&
|
|
613
|
+
!containsAny(uncastType) &&
|
|
614
|
+
anyInvolvedInContextualCheck &&
|
|
615
|
+
(castIsAny || !genericsMismatch(uncastType, contextualType)) &&
|
|
616
|
+
(contextualTypeIsAny ||
|
|
617
|
+
checker.isTypeAssignableTo(uncastType, contextualType)) &&
|
|
618
|
+
!isNullishLiteralToUnion;
|
|
619
|
+
if (isContextuallyUnnecessary) {
|
|
620
|
+
context.report({
|
|
621
|
+
node,
|
|
622
|
+
messageId: 'contextuallyUnnecessary',
|
|
623
|
+
fix: createAssertionFixer(node),
|
|
624
|
+
});
|
|
625
|
+
return;
|
|
626
|
+
}
|
|
260
627
|
}
|
|
261
|
-
|
|
628
|
+
reportDoubleAssertionIfUnnecessary(node, contextualType);
|
|
262
629
|
},
|
|
263
630
|
TSNonNullExpression(node) {
|
|
264
631
|
const removeExclamationFix = fixer => {
|
|
@@ -234,13 +234,13 @@ exports.default = (0, util_1.createRule)({
|
|
|
234
234
|
/** Given type parameters, returns a function to test whether a type is one of those parameters. */
|
|
235
235
|
function getIsTypeParameter(typeParameters) {
|
|
236
236
|
if (typeParameters == null) {
|
|
237
|
-
return (
|
|
237
|
+
return () => false;
|
|
238
238
|
}
|
|
239
239
|
const set = new Set();
|
|
240
240
|
for (const t of typeParameters.params) {
|
|
241
241
|
set.add(t.name.name);
|
|
242
242
|
}
|
|
243
|
-
return
|
|
243
|
+
return typeName => set.has(typeName);
|
|
244
244
|
}
|
|
245
245
|
/** True if any of the outer type parameters are used in a signature. */
|
|
246
246
|
function signatureUsesTypeParameter(sig, isTypeParameter) {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@typescript-eslint/eslint-plugin",
|
|
3
|
-
"version": "8.58.3-alpha.
|
|
3
|
+
"version": "8.58.3-alpha.5",
|
|
4
4
|
"description": "TypeScript plugin for ESLint",
|
|
5
5
|
"files": [
|
|
6
6
|
"dist",
|
|
@@ -52,10 +52,10 @@
|
|
|
52
52
|
"ignore": "^7.0.5",
|
|
53
53
|
"natural-compare": "^1.4.0",
|
|
54
54
|
"ts-api-utils": "^2.5.0",
|
|
55
|
-
"@typescript-eslint/scope-manager": "8.58.3-alpha.
|
|
56
|
-
"@typescript-eslint/type-utils": "8.58.3-alpha.
|
|
57
|
-
"@typescript-eslint/utils": "8.58.3-alpha.
|
|
58
|
-
"@typescript-eslint/visitor-keys": "8.58.3-alpha.
|
|
55
|
+
"@typescript-eslint/scope-manager": "8.58.3-alpha.5",
|
|
56
|
+
"@typescript-eslint/type-utils": "8.58.3-alpha.5",
|
|
57
|
+
"@typescript-eslint/utils": "8.58.3-alpha.5",
|
|
58
|
+
"@typescript-eslint/visitor-keys": "8.58.3-alpha.5"
|
|
59
59
|
},
|
|
60
60
|
"devDependencies": {
|
|
61
61
|
"@types/json-schema": "^7.0.15",
|
|
@@ -78,13 +78,13 @@
|
|
|
78
78
|
"typescript": ">=4.8.4 <6.1.0",
|
|
79
79
|
"unist-util-visit": "^5.0.0",
|
|
80
80
|
"vitest": "^4.0.18",
|
|
81
|
-
"@typescript-eslint/rule-schema-to-typescript-types": "8.58.3-alpha.
|
|
82
|
-
"@typescript-eslint/rule-tester": "8.58.3-alpha.
|
|
81
|
+
"@typescript-eslint/rule-schema-to-typescript-types": "8.58.3-alpha.5",
|
|
82
|
+
"@typescript-eslint/rule-tester": "8.58.3-alpha.5"
|
|
83
83
|
},
|
|
84
84
|
"peerDependencies": {
|
|
85
85
|
"eslint": "^8.57.0 || ^9.0.0 || ^10.0.0",
|
|
86
86
|
"typescript": ">=4.8.4 <6.1.0",
|
|
87
|
-
"@typescript-eslint/parser": "^8.58.3-alpha.
|
|
87
|
+
"@typescript-eslint/parser": "^8.58.3-alpha.5"
|
|
88
88
|
},
|
|
89
89
|
"funding": {
|
|
90
90
|
"type": "opencollective",
|