-
Notifications
You must be signed in to change notification settings - Fork 0
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
build(deps-dev): bump @typescript-eslint/eslint-plugin from 8.3.0 to 8.8.0 #1347
Merged
github-actions
merged 1 commit into
main
from
dependabot/npm_and_yarn/typescript-eslint/eslint-plugin-8.8.0
Oct 1, 2024
Merged
build(deps-dev): bump @typescript-eslint/eslint-plugin from 8.3.0 to 8.8.0 #1347
github-actions
merged 1 commit into
main
from
dependabot/npm_and_yarn/typescript-eslint/eslint-plugin-8.8.0
Oct 1, 2024
Conversation
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Bumps [@typescript-eslint/eslint-plugin](https://github.com/typescript-eslint/typescript-eslint/tree/HEAD/packages/eslint-plugin) from 8.3.0 to 8.8.0. - [Release notes](https://github.com/typescript-eslint/typescript-eslint/releases) - [Changelog](https://github.com/typescript-eslint/typescript-eslint/blob/main/packages/eslint-plugin/CHANGELOG.md) - [Commits](https://github.com/typescript-eslint/typescript-eslint/commits/v8.8.0/packages/eslint-plugin) --- updated-dependencies: - dependency-name: "@typescript-eslint/eslint-plugin" dependency-type: direct:development update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] <support@github.com>
Diff between @typescript-eslint/eslint-plugin 8.3.0 and 8.8.0diff --git a/dist/rules/await-thenable.js b/dist/rules/await-thenable.js
index v8.3.0..v8.8.0 100644
--- a/dist/rules/await-thenable.js
+++ b/dist/rules/await-thenable.js
@@ -26,4 +26,5 @@
const tsutils = __importStar(require("ts-api-utils"));
const util_1 = require("../util");
+const getForStatementHeadLoc_1 = require("../util/getForStatementHeadLoc");
exports.default = (0, util_1.createRule)({
name: 'await-thenable',
@@ -37,5 +38,7 @@
messages: {
await: 'Unexpected `await` of a non-Promise (non-"Thenable") value.',
+ forAwaitOfNonThenable: 'Unexpected `for await...of` of a value that is not async iterable.',
removeAwait: 'Remove unnecessary `await`.',
+ convertToOrdinaryFor: 'Convert to an ordinary `for...of` loop.',
},
schema: [],
@@ -69,4 +72,28 @@
}
},
+ 'ForOfStatement[await=true]'(node) {
+ const type = services.getTypeAtLocation(node.right);
+ if ((0, util_1.isTypeAnyType)(type)) {
+ return;
+ }
+ const asyncIteratorSymbol = tsutils.getWellKnownSymbolPropertyOfType(type, 'asyncIterator', checker);
+ if (asyncIteratorSymbol == null) {
+ context.report({
+ loc: (0, getForStatementHeadLoc_1.getForStatementHeadLoc)(context.sourceCode, node),
+ messageId: 'forAwaitOfNonThenable',
+ suggest: [
+ // Note that this suggestion causes broken code for sync iterables
+ // of promises, since the loop variable is not awaited.
+ {
+ messageId: 'convertToOrdinaryFor',
+ fix(fixer) {
+ const awaitToken = (0, util_1.nullThrows)(context.sourceCode.getFirstToken(node, util_1.isAwaitKeyword), util_1.NullThrowsReasons.MissingToken('await', 'for await loop'));
+ return fixer.remove(awaitToken);
+ },
+ },
+ ],
+ });
+ }
+ },
};
},
diff --git a/dist/rules/ban-ts-comment.js b/dist/rules/ban-ts-comment.js
index v8.3.0..v8.8.0 100644
--- a/dist/rules/ban-ts-comment.js
+++ b/dist/rules/ban-ts-comment.js
@@ -52,4 +52,5 @@
'ts-check': { $ref: '#/items/0/$defs/directiveConfigSchema' },
minimumDescriptionLength: {
+ description: 'A minimum character length for descriptions when `allow-with-description` is enabled.',
type: 'number',
default: defaultMinimumDescriptionLength,
diff --git a/dist/rules/class-literal-property-style.js b/dist/rules/class-literal-property-style.js
index v8.3.0..v8.8.0 100644
--- a/dist/rules/class-literal-property-style.js
+++ b/dist/rules/class-literal-property-style.js
@@ -41,7 +41,4 @@
create(context, [style]) {
const propertiesInfoStack = [];
- function getStringValue(node) {
- return (0, util_1.getStaticStringValue)(node) ?? context.sourceCode.getText(node);
- }
function enterClassBody() {
propertiesInfoStack.push({
@@ -57,6 +54,6 @@
return;
}
- const name = getStringValue(node.key);
- if (excludeSet.has(name)) {
+ const name = (0, util_1.getStaticMemberAccessValue)(node, context);
+ if (name && excludeSet.has(name)) {
return;
}
@@ -83,6 +80,5 @@
if ((0, util_1.isAssignee)(node)) {
const { excludeSet } = propertiesInfoStack[propertiesInfoStack.length - 1];
- const name = (0, util_1.getStaticStringValue)(node.property) ??
- context.sourceCode.getText(node.property);
+ const name = (0, util_1.getStaticMemberAccessValue)(node, context);
if (name) {
excludeSet.add(name);
@@ -106,10 +102,11 @@
return;
}
- const name = getStringValue(node.key);
- const hasDuplicateKeySetter = node.parent.body.some(element => {
- return (element.type === utils_1.AST_NODE_TYPES.MethodDefinition &&
- element.kind === 'set' &&
- getStringValue(element.key) === name);
- });
+ const name = (0, util_1.getStaticMemberAccessValue)(node, context);
+ const hasDuplicateKeySetter = name &&
+ node.parent.body.some(element => {
+ return (element.type === utils_1.AST_NODE_TYPES.MethodDefinition &&
+ element.kind === 'set' &&
+ (0, util_1.isStaticMemberAccessOfValue)(element, context, name));
+ });
if (hasDuplicateKeySetter) {
return;
diff --git a/dist/rules/class-methods-use-this.js b/dist/rules/class-methods-use-this.js
index v8.3.0..v8.8.0 100644
--- a/dist/rules/class-methods-use-this.js
+++ b/dist/rules/class-methods-use-this.js
@@ -121,8 +121,6 @@
}
const hashIfNeeded = node.key.type === utils_1.AST_NODE_TYPES.PrivateIdentifier ? '#' : '';
- const name = node.key.type === utils_1.AST_NODE_TYPES.Literal
- ? (0, util_1.getStaticStringValue)(node.key)
- : node.key.name || '';
- return !exceptMethods.has(hashIfNeeded + (name ?? ''));
+ const name = (0, util_1.getStaticMemberAccessValue)(node, context);
+ return (typeof name !== 'string' || !exceptMethods.has(hashIfNeeded + name));
}
/**
diff --git a/dist/rules/consistent-type-assertions.js b/dist/rules/consistent-type-assertions.js
index v8.3.0..v8.8.0 100644
--- a/dist/rules/consistent-type-assertions.js
+++ b/dist/rules/consistent-type-assertions.js
@@ -53,4 +53,5 @@
properties: {
assertionStyle: {
+ description: 'The expected assertion style to enforce.',
type: 'string',
enum: ['never'],
@@ -64,8 +65,10 @@
properties: {
assertionStyle: {
+ description: 'The expected assertion style to enforce.',
type: 'string',
enum: ['as', 'angle-bracket'],
},
objectLiteralTypeAssertions: {
+ description: 'Whether to always prefer type declarations for object literals used as variable initializers, rather than type assertions.',
type: 'string',
enum: ['allow', 'allow-as-parameter', 'never'],
@@ -86,5 +89,4 @@
],
create(context, [options]) {
- const parserServices = (0, util_1.getParserServices)(context, true);
function isConst(node) {
if (node.type !== utils_1.AST_NODE_TYPES.TSTypeReference) {
@@ -108,5 +110,6 @@
fix: messageId === 'as'
? (fixer) => {
- const tsNode = parserServices.esTreeNodeToTSNodeMap.get(node);
+ // lazily access parserServices to avoid crashing on non TS files (#9860)
+ const tsNode = (0, util_1.getParserServices)(context, true).esTreeNodeToTSNodeMap.get(node);
const expressionCode = context.sourceCode.getText(node.expression);
const typeAnnotationCode = context.sourceCode.getText(node.typeAnnotation);
diff --git a/dist/rules/consistent-type-exports.js b/dist/rules/consistent-type-exports.js
index v8.3.0..v8.8.0 100644
--- a/dist/rules/consistent-type-exports.js
+++ b/dist/rules/consistent-type-exports.js
@@ -1,6 +1,30 @@
"use strict";
+var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
+ if (k2 === undefined) k2 = k;
+ var desc = Object.getOwnPropertyDescriptor(m, k);
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
+ desc = { enumerable: true, get: function() { return m[k]; } };
+ }
+ Object.defineProperty(o, k2, desc);
+}) : (function(o, m, k, k2) {
+ if (k2 === undefined) k2 = k;
+ o[k2] = m[k];
+}));
+var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
+}) : function(o, v) {
+ o["default"] = v;
+});
+var __importStar = (this && this.__importStar) || function (mod) {
+ if (mod && mod.__esModule) return mod;
+ var result = {};
+ if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
+ __setModuleDefault(result, mod);
+ return result;
+};
Object.defineProperty(exports, "__esModule", { value: true });
const utils_1 = require("@typescript-eslint/utils");
-const typescript_1 = require("typescript");
+const tsutils = __importStar(require("ts-api-utils"));
+const ts = __importStar(require("typescript"));
const util_1 = require("../util");
exports.default = (0, util_1.createRule)({
@@ -22,4 +46,5 @@
properties: {
fixMixedExportsWithInlineTypeSpecifier: {
+ description: 'Whether the rule will autofix "mixed" export cases using TS inline type specifiers.',
type: 'boolean',
},
@@ -38,6 +63,7 @@
const sourceExportsMap = {};
const services = (0, util_1.getParserServices)(context);
+ const checker = services.program.getTypeChecker();
/**
- * Helper for identifying if an export specifier resolves to a
+ * Helper for identifying if a symbol resolves to a
* JavaScript value or a TypeScript type.
*
@@ -45,18 +71,69 @@
* can't be resolved.
*/
- function isSpecifierTypeBased(specifier) {
- const checker = services.program.getTypeChecker();
- const symbol = services.getSymbolAtLocation(specifier.exported);
+ function isSymbolTypeBased(symbol) {
if (!symbol) {
return undefined;
}
- const aliasedSymbol = checker.getAliasedSymbol(symbol);
- // eslint-disable-next-line @typescript-eslint/no-unsafe-enum-comparison
- if (aliasedSymbol.escapedName === 'unknown') {
+ const aliasedSymbol = tsutils.isSymbolFlagSet(symbol, ts.SymbolFlags.Alias)
+ ? checker.getAliasedSymbol(symbol)
+ : symbol;
+ if (checker.isUnknownSymbol(aliasedSymbol)) {
return undefined;
}
- return !(aliasedSymbol.flags & typescript_1.SymbolFlags.Value);
+ return !(aliasedSymbol.flags & ts.SymbolFlags.Value);
}
return {
+ ExportAllDeclaration(node) {
+ if (node.exportKind === 'type') {
+ return;
+ }
+ const sourceModule = ts.resolveModuleName(node.source.value, context.filename, services.program.getCompilerOptions(), ts.sys);
+ if (sourceModule.resolvedModule == null) {
+ return;
+ }
+ const sourceFile = services.program.getSourceFile(sourceModule.resolvedModule.resolvedFileName);
+ if (sourceFile == null) {
+ return;
+ }
+ const sourceFileSymbol = checker.getSymbolAtLocation(sourceFile);
+ if (sourceFileSymbol == null) {
+ return;
+ }
+ const sourceFileType = checker.getTypeOfSymbol(sourceFileSymbol);
+ // Module can explicitly export types or values, and it's not difficult
+ // to distinguish one from the other, since we can get the flags of
+ // the exported symbols or check if symbol export declaration has
+ // the "type" keyword in it.
+ //
+ // Things get a lot more complicated when we're dealing with
+ // export * from './module-with-type-only-exports'
+ // export type * from './module-with-type-and-value-exports'
+ //
+ // TS checker has an internal function getExportsOfModuleWorker that
+ // recursively visits all module exports, including "export *". It then
+ // puts type-only-star-exported symbols into the typeOnlyExportStarMap
+ // property of sourceFile's SymbolLinks. Since symbol links aren't
+ // exposed outside the checker, we cannot access it directly.
+ //
+ // Therefore, to filter out value properties, we use the following hack:
+ // checker.getPropertiesOfType returns all exports that were originally
+ // values, but checker.getPropertyOfType returns undefined for
+ // properties that are mentioned in the typeOnlyExportStarMap.
+ const isThereAnyExportedValue = checker
+ .getPropertiesOfType(sourceFileType)
+ .some(propertyTypeSymbol => checker.getPropertyOfType(sourceFileType, propertyTypeSymbol.escapedName.toString()) != null);
+ if (isThereAnyExportedValue) {
+ return;
+ }
+ context.report({
+ node,
+ messageId: 'typeOverValue',
+ fix(fixer) {
+ const asteriskToken = (0, util_1.nullThrows)(context.sourceCode.getFirstToken(node, token => token.type === utils_1.AST_TOKEN_TYPES.Punctuator &&
+ token.value === '*'), util_1.NullThrowsReasons.MissingToken('asterisk', 'export all declaration'));
+ return fixer.insertTextBefore(asteriskToken, 'type ');
+ },
+ });
+ },
ExportNamedDeclaration(node) {
// Coerce the source into a string for use as a lookup entry.
@@ -93,5 +170,5 @@
continue;
}
- const isTypeBased = isSpecifierTypeBased(specifier);
+ const isTypeBased = isSymbolTypeBased(services.getSymbolAtLocation(specifier.exported));
if (isTypeBased === true) {
typeBasedSpecifiers.push(specifier);
@@ -199,5 +276,5 @@
function* fixSeparateNamedExports(fixer, sourceCode, report) {
const { node, typeBasedSpecifiers, inlineTypeSpecifiers, valueSpecifiers } = report;
- const typeSpecifiers = typeBasedSpecifiers.concat(inlineTypeSpecifiers);
+ const typeSpecifiers = [...typeBasedSpecifiers, ...inlineTypeSpecifiers];
const source = getSourceFromExport(node);
const specifierNames = typeSpecifiers.map(getSpecifierText).join(', ');
diff --git a/dist/rules/consistent-type-imports.js b/dist/rules/consistent-type-imports.js
index v8.3.0..v8.8.0 100644
--- a/dist/rules/consistent-type-imports.js
+++ b/dist/rules/consistent-type-imports.js
@@ -21,11 +21,14 @@
properties: {
disallowTypeAnnotations: {
+ description: 'Whether to disallow type imports in type annotations (`import()`).',
type: 'boolean',
},
fixStyle: {
+ description: 'The expected type modifier to be added when an import is detected as used only in the type position.',
type: 'string',
enum: ['separate-type-imports', 'inline-type-imports'],
},
prefer: {
+ description: 'The expected import kind for type-only imports.',
type: 'string',
enum: ['type-imports', 'no-type-imports'],
diff --git a/dist/rules/dot-notation.js b/dist/rules/dot-notation.js
index v8.3.0..v8.8.0 100644
--- a/dist/rules/dot-notation.js
+++ b/dist/rules/dot-notation.js
@@ -45,20 +45,25 @@
properties: {
allowKeywords: {
+ description: 'Whether to allow keywords such as ["class"]`.',
type: 'boolean',
default: true,
},
allowPattern: {
+ description: 'Regular expression of names to allow.',
type: 'string',
default: '',
},
allowPrivateClassPropertyAccess: {
+ description: 'Whether to allow accessing class members marked as `private` with array notation.',
type: 'boolean',
default: false,
},
allowProtectedClassPropertyAccess: {
+ description: 'Whether to allow accessing class members marked as `protected` with array notation.',
type: 'boolean',
default: false,
},
allowIndexSignaturePropertyAccess: {
+ description: 'Whether to allow accessing properties matching an index signature with array notation.',
type: 'boolean',
default: false,
diff --git a/dist/rules/explicit-member-accessibility.js b/dist/rules/explicit-member-accessibility.js
index v8.3.0..v8.8.0 100644
--- a/dist/rules/explicit-member-accessibility.js
+++ b/dist/rules/explicit-member-accessibility.js
@@ -4,4 +4,5 @@
const util_1 = require("../util");
const getMemberHeadLoc_1 = require("../util/getMemberHeadLoc");
+const rangeToLoc_1 = require("../util/rangeToLoc");
exports.default = (0, util_1.createRule)({
name: 'explicit-member-accessibility',
@@ -59,4 +60,5 @@
},
ignoredMethodNames: {
+ description: 'Specific method names that may be ignored.',
type: 'array',
items: {
@@ -110,5 +112,5 @@
const publicKeyword = findPublicKeyword(methodDefinition);
context.report({
- loc: rangeToLoc(context.sourceCode, publicKeyword.range),
+ loc: (0, rangeToLoc_1.rangeToLoc)(context.sourceCode, publicKeyword.range),
messageId: 'unwantedPublicAccessibility',
data: {
@@ -209,5 +211,5 @@
const publicKeywordRange = findPublicKeyword(propertyDefinition);
context.report({
- loc: rangeToLoc(context.sourceCode, publicKeywordRange.range),
+ loc: (0, rangeToLoc_1.rangeToLoc)(context.sourceCode, publicKeywordRange.range),
messageId: 'unwantedPublicAccessibility',
data: {
@@ -265,5 +267,5 @@
const publicKeyword = findPublicKeyword(node);
context.report({
- loc: rangeToLoc(context.sourceCode, publicKeyword.range),
+ loc: (0, rangeToLoc_1.rangeToLoc)(context.sourceCode, publicKeyword.range),
messageId: 'unwantedPublicAccessibility',
data: {
@@ -285,9 +287,3 @@
},
});
-function rangeToLoc(sourceCode, range) {
- return {
- start: sourceCode.getLocFromIndex(range[0]),
- end: sourceCode.getLocFromIndex(range[1]),
- };
-}
//# sourceMappingURL=explicit-member-accessibility.js.map
\ No newline at end of file
diff --git a/dist/rules/explicit-module-boundary-types.js b/dist/rules/explicit-module-boundary-types.js
index v8.3.0..v8.8.0 100644
--- a/dist/rules/explicit-module-boundary-types.js
+++ b/dist/rules/explicit-module-boundary-types.js
@@ -208,15 +208,5 @@
(node.type === utils_1.AST_NODE_TYPES.Property && node.method) ||
node.type === utils_1.AST_NODE_TYPES.PropertyDefinition) {
- if (node.key.type === utils_1.AST_NODE_TYPES.Literal &&
- typeof node.key.value === 'string') {
- return options.allowedNames.includes(node.key.value);
- }
- if (node.key.type === utils_1.AST_NODE_TYPES.TemplateLiteral &&
- node.key.expressions.length === 0) {
- return options.allowedNames.includes(node.key.quasis[0].value.raw);
- }
- if (!node.computed && node.key.type === utils_1.AST_NODE_TYPES.Identifier) {
- return options.allowedNames.includes(node.key.name);
- }
+ return (0, util_1.isStaticMemberAccessOfValue)(node, context, ...options.allowedNames);
}
return false;
diff --git a/dist/util/index.js b/dist/util/index.js
index v8.3.0..v8.8.0 100644
--- a/dist/util/index.js
+++ b/dist/util/index.js
@@ -38,4 +38,5 @@
__exportStar(require("./isAssignee"), exports);
__exportStar(require("./getFixOrSuggest"), exports);
+__exportStar(require("./isArrayMethodCallWithPredicate"), exports);
// this is done for convenience - saves migrating all of the old rules
__exportStar(require("@typescript-eslint/type-utils"), exports);
diff --git a/dist/rules/max-params.js b/dist/rules/max-params.js
index v8.3.0..v8.8.0 100644
--- a/dist/rules/max-params.js
+++ b/dist/rules/max-params.js
@@ -17,13 +17,16 @@
type: 'object',
properties: {
- maximum: {
+ max: {
+ description: 'A maximum number of parameters in function definitions.',
type: 'integer',
minimum: 0,
},
- max: {
+ maximum: {
+ description: '(deprecated) A maximum number of parameters in function definitions.',
type: 'integer',
minimum: 0,
},
countVoidThis: {
+ description: 'Whether to count a `this` declaration when the type is `void`.',
type: 'boolean',
},
diff --git a/dist/rules/member-ordering.js b/dist/rules/member-ordering.js
index v8.3.0..v8.8.0 100644
--- a/dist/rules/member-ordering.js
+++ b/dist/rules/member-ordering.js
@@ -172,58 +172,59 @@
'method',
];
-const allMemberTypes = Array.from([
- 'readonly-signature',
- 'signature',
- 'readonly-field',
- 'field',
- 'method',
- 'call-signature',
- 'constructor',
- 'accessor',
- 'get',
- 'set',
- 'static-initialization',
-].reduce((all, type) => {
- all.add(type);
- ['public', 'protected', 'private', '#private'].forEach(accessibility => {
- if (type !== 'readonly-signature' &&
- type !== 'signature' &&
- type !== 'static-initialization' &&
- type !== 'call-signature' &&
- !(type === 'constructor' && accessibility === '#private')) {
- all.add(`${accessibility}-${type}`); // e.g. `public-field`
- }
- // Only class instance fields, methods, accessors, get and set can have decorators attached to them
- if (accessibility !== '#private' &&
- (type === 'readonly-field' ||
- type === 'field' ||
- type === 'method' ||
- type === 'accessor' ||
- type === 'get' ||
- type === 'set')) {
- all.add(`${accessibility}-decorated-${type}`);
- all.add(`decorated-${type}`);
- }
- if (type !== 'constructor' &&
+const allMemberTypes = [
+ ...new Set([
+ 'readonly-signature',
+ 'signature',
+ 'readonly-field',
+ 'field',
+ 'method',
+ 'call-signature',
+ 'constructor',
+ 'accessor',
+ 'get',
+ 'set',
+ 'static-initialization',
+ ].flatMap(type => [
+ type,
+ ...['public', 'protected', 'private', '#private']
+ .flatMap(accessibility => [
type !== 'readonly-signature' &&
- type !== 'signature' &&
- type !== 'call-signature') {
- // There is no `static-constructor` or `instance-constructor` or `abstract-constructor`
- if (accessibility === '#private' || accessibility === 'private') {
- ['static', 'instance'].forEach(scope => {
- all.add(`${scope}-${type}`);
- all.add(`${accessibility}-${scope}-${type}`);
- });
- }
- else {
- ['static', 'instance', 'abstract'].forEach(scope => {
- all.add(`${scope}-${type}`);
- all.add(`${accessibility}-${scope}-${type}`);
- });
- }
- }
- });
- return all;
-}, new Set()));
+ type !== 'signature' &&
+ type !== 'static-initialization' &&
+ type !== 'call-signature' &&
+ !(type === 'constructor' && accessibility === '#private')
+ ? `${accessibility}-${type}` // e.g. `public-field`
+ : [],
+ // Only class instance fields, methods, accessors, get and set can have decorators attached to them
+ accessibility !== '#private' &&
+ (type === 'readonly-field' ||
+ type === 'field' ||
+ type === 'method' ||
+ type === 'accessor' ||
+ type === 'get' ||
+ type === 'set')
+ ? [`${accessibility}-decorated-${type}`, `decorated-${type}`]
+ : [],
+ type !== 'constructor' &&
+ type !== 'readonly-signature' &&
+ type !== 'signature' &&
+ type !== 'call-signature'
+ ? [
+ 'static',
+ 'instance',
+ // There is no `static-constructor` or `instance-constructor` or `abstract-constructor`
+ ...(accessibility === '#private' ||
+ accessibility === 'private'
+ ? []
+ : ['abstract']),
+ ].flatMap(scope => [
+ `${scope}-${type}`,
+ `${accessibility}-${scope}-${type}`,
+ ])
+ : [],
+ ])
+ .flat(),
+ ])),
+];
const functionExpressions = [
utils_1.AST_NODE_TYPES.FunctionExpression,
@@ -344,5 +345,5 @@
function getRankOrder(memberGroups, orderConfig) {
let rank = -1;
- const stack = memberGroups.slice(); // Get a copy of the member groups
+ const stack = [...memberGroups]; // Get a copy of the member groups
while (stack.length > 0 && rank === -1) {
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
diff --git a/dist/util/misc.js b/dist/util/misc.js
index v8.3.0..v8.8.0 100644
--- a/dist/util/misc.js
+++ b/dist/util/misc.js
@@ -1,6 +1,3 @@
"use strict";
-/**
- * @fileoverview Really small utility functions that didn't deserve their own files
- */
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
@@ -27,5 +24,5 @@
};
Object.defineProperty(exports, "__esModule", { value: true });
-exports.MemberNameType = void 0;
+exports.MemberNameType = exports.isStaticMemberAccessOfValue = void 0;
exports.arrayGroupByToMap = arrayGroupByToMap;
exports.arraysAreEqual = arraysAreEqual;
@@ -33,4 +30,5 @@
exports.formatWordList = formatWordList;
exports.getEnumNames = getEnumNames;
+exports.getStaticMemberAccessValue = getStaticMemberAccessValue;
exports.getNameFromIndexSignature = getNameFromIndexSignature;
exports.getNameFromMember = getNameFromMember;
@@ -41,4 +39,7 @@
exports.upperCaseFirst = upperCaseFirst;
exports.findLastIndex = findLastIndex;
+/**
+ * @fileoverview Really small utility functions that didn't deserve their own files
+ */
const type_utils_1 = require("@typescript-eslint/type-utils");
const utils_1 = require("@typescript-eslint/utils");
@@ -196,3 +197,70 @@
return (node.params.length === 1 && !(0, astUtils_1.isParenthesized)(node.params[0], sourceCode));
}
+/**
+ * Gets a member being accessed or declared if its value can be determined statically, and
+ * resolves it to the string or symbol value that will be used as the actual member
+ * access key at runtime. Otherwise, returns `undefined`.
+ *
+ * ```ts
+ * x.member // returns 'member'
+ * ^^^^^^^^
+ *
+ * x?.member // returns 'member' (optional chaining is treated the same)
+ * ^^^^^^^^^
+ *
+ * x['value'] // returns 'value'
+ * ^^^^^^^^^^
+ *
+ * x[Math.random()] // returns undefined (not a static value)
+ * ^^^^^^^^^^^^^^^^
+ *
+ * arr[0] // returns '0' (NOT 0)
+ * ^^^^^^
+ *
+ * arr[0n] // returns '0' (NOT 0n)
+ * ^^^^^^^
+ *
+ * const s = Symbol.for('symbolName')
+ * x[s] // returns `Symbol.for('symbolName')` (since it's a static/global symbol)
+ * ^^^^
+ *
+ * const us = Symbol('symbolName')
+ * x[us] // returns undefined (since it's a unique symbol, so not statically analyzable)
+ * ^^^^^
+ *
+ * var object = {
+ * 1234: '4567', // returns '1234' (NOT 1234)
+ * ^^^^^^^^^^^^
+ * method() { } // returns 'method'
+ * ^^^^^^^^^^^^
+ * }
+ *
+ * class WithMembers {
+ * foo: string // returns 'foo'
+ * ^^^^^^^^^^^
+ * }
+ * ```
+ */
+function getStaticMemberAccessValue(node, { sourceCode }) {
+ const key = node.type === utils_1.AST_NODE_TYPES.MemberExpression ? node.property : node.key;
+ const { type } = key;
+ if (!node.computed &&
+ (type === utils_1.AST_NODE_TYPES.Identifier ||
+ type === utils_1.AST_NODE_TYPES.PrivateIdentifier)) {
+ return key.name;
+ }
+ const result = (0, astUtils_1.getStaticValue)(key, sourceCode.getScope(node));
+ if (!result) {
+ return undefined;
+ }
+ const { value } = result;
+ return typeof value === 'symbol' ? value : String(value);
+}
+/**
+ * Answers whether the member expression looks like
+ * `x.value`, `x['value']`,
+ * or even `const v = 'value'; x[v]` (or optional variants thereof).
+ */
+const isStaticMemberAccessOfValue = (memberExpression, context, ...values) => values.includes(getStaticMemberAccessValue(memberExpression, context));
+exports.isStaticMemberAccessOfValue = isStaticMemberAccessOfValue;
//# sourceMappingURL=misc.js.map
\ No newline at end of file
diff --git a/dist/rules/no-base-to-string.js b/dist/rules/no-base-to-string.js
index v8.3.0..v8.8.0 100644
--- a/dist/rules/no-base-to-string.js
+++ b/dist/rules/no-base-to-string.js
@@ -49,4 +49,5 @@
properties: {
ignoredTypeNames: {
+ description: 'Stringified regular expressions of type names to ignore.',
type: 'array',
items: {
diff --git a/dist/rules/no-confusing-non-null-assertion.js b/dist/rules/no-confusing-non-null-assertion.js
index v8.3.0..v8.8.0 100644
--- a/dist/rules/no-confusing-non-null-assertion.js
+++ b/dist/rules/no-confusing-non-null-assertion.js
@@ -3,4 +3,14 @@
const utils_1 = require("@typescript-eslint/utils");
const util_1 = require("../util");
+const confusingOperators = new Set([
+ '=',
+ '==',
+ '===',
+ 'in',
+ 'instanceof',
+]);
+function isConfusingOperator(operator) {
+ return confusingOperators.has(operator);
+}
exports.default = (0, util_1.createRule)({
name: 'no-confusing-non-null-assertion',
@@ -13,9 +23,11 @@
hasSuggestions: true,
messages: {
- confusingEqual: 'Confusing combinations of non-null assertion and equal test like "a! == b", which looks very similar to not equal "a !== b".',
- confusingAssign: 'Confusing combinations of non-null assertion and equal test like "a! = b", which looks very similar to not equal "a != b".',
- notNeedInEqualTest: 'Unnecessary non-null assertion (!) in equal test.',
- notNeedInAssign: 'Unnecessary non-null assertion (!) in assignment left hand.',
- wrapUpLeft: 'Wrap up left hand to avoid putting non-null assertion "!" and "=" together.',
+ confusingEqual: 'Confusing combination of non-null assertion and equality test like `a! == b`, which looks very similar to `a !== b`.',
+ confusingAssign: 'Confusing combination of non-null assertion and assignment like `a! = b`, which looks very similar to `a != b`.',
+ confusingOperator: 'Confusing combination of non-null assertion and `{{operator}}` operator like `a! {{operator}} b`, which might be misinterpreted as `!(a {{operator}} b)`.',
+ notNeedInEqualTest: 'Remove unnecessary non-null assertion (!) in equality test.',
+ notNeedInAssign: 'Remove unnecessary non-null assertion (!) in assignment left-hand side.',
+ notNeedInOperator: 'Remove possibly unnecessary non-null assertion (!) in the left operand of the `{{operator}}` operator.',
+ wrapUpLeft: 'Wrap the left-hand side in parentheses to avoid confusion with "{{operator}}" operator.',
},
schema: [],
@@ -23,13 +35,34 @@
defaultOptions: [],
create(context) {
+ function confusingOperatorToMessageData(operator) {
+ switch (operator) {
+ case '=':
+ return {
+ messageId: 'confusingAssign',
+ };
+ case '==':
+ case '===':
+ return {
+ messageId: 'confusingEqual',
+ };
+ case 'in':
+ case 'instanceof':
+ return {
+ messageId: 'confusingOperator',
+ data: { operator },
+ };
+ // istanbul ignore next
+ default:
+ operator;
+ throw new Error(`Unexpected operator ${operator}`);
+ }
+ }
return {
'BinaryExpression, AssignmentExpression'(node) {
- function isLeftHandPrimaryExpression(node) {
- return node.type === utils_1.AST_NODE_TYPES.TSNonNullExpression;
- }
- if (node.operator === '==' ||
- node.operator === '===' ||
- node.operator === '=') {
- const isAssign = node.operator === '=';
+ const operator = node.operator;
+ if (isConfusingOperator(operator)) {
+ // Look for a non-null assertion as the last token on the left hand side.
+ // That way, we catch things like `1 + two! === 3`, even though the left
+ // hand side isn't a non-null assertion AST node.
const leftHandFinalToken = context.sourceCode.getLastToken(node.left);
const tokenAfterLeft = context.sourceCode.getTokenAfter(node.left);
@@ -37,18 +70,48 @@
leftHandFinalToken.value === '!' &&
tokenAfterLeft?.value !== ')') {
- if (isLeftHandPrimaryExpression(node.left)) {
+ if (node.left.type === utils_1.AST_NODE_TYPES.TSNonNullExpression) {
+ let suggestions;
+ switch (operator) {
+ case '=':
+ suggestions = [
+ {
+ messageId: 'notNeedInAssign',
+ fix: (fixer) => fixer.remove(leftHandFinalToken),
+ },
+ ];
+ break;
+ case '==':
+ case '===':
+ suggestions = [
+ {
+ messageId: 'notNeedInEqualTest',
+ fix: (fixer) => fixer.remove(leftHandFinalToken),
+ },
+ ];
+ break;
+ case 'in':
+ case 'instanceof':
+ suggestions = [
+ {
+ messageId: 'notNeedInOperator',
+ data: { operator },
+ fix: (fixer) => fixer.remove(leftHandFinalToken),
+ },
+ {
+ messageId: 'wrapUpLeft',
+ data: { operator },
+ fix: wrapUpLeftFixer(node),
+ },
+ ];
+ break;
+ // istanbul ignore next
+ default:
+ operator;
+ return;
+ }
context.report({
node,
- messageId: isAssign ? 'confusingAssign' : 'confusingEqual',
- suggest: [
- {
- messageId: isAssign
- ? 'notNeedInAssign'
- : 'notNeedInEqualTest',
- fix: (fixer) => [
- fixer.remove(leftHandFinalToken),
- ],
- },
- ],
+ ...confusingOperatorToMessageData(operator),
+ suggest: suggestions,
});
}
@@ -56,12 +119,10 @@
context.report({
node,
- messageId: isAssign ? 'confusingAssign' : 'confusingEqual',
+ ...confusingOperatorToMessageData(operator),
suggest: [
{
messageId: 'wrapUpLeft',
- fix: (fixer) => [
- fixer.insertTextBefore(node.left, '('),
- fixer.insertTextAfter(node.left, ')'),
- ],
+ data: { operator },
+ fix: wrapUpLeftFixer(node),
},
],
@@ -74,3 +135,9 @@
},
});
+function wrapUpLeftFixer(node) {
+ return (fixer) => [
+ fixer.insertTextBefore(node.left, '('),
+ fixer.insertTextAfter(node.left, ')'),
+ ];
+}
//# sourceMappingURL=no-confusing-non-null-assertion.js.map
\ No newline at end of file
diff --git a/dist/rules/no-confusing-void-expression.js b/dist/rules/no-confusing-void-expression.js
index v8.3.0..v8.8.0 100644
--- a/dist/rules/no-confusing-void-expression.js
+++ b/dist/rules/no-confusing-void-expression.js
@@ -58,6 +58,12 @@
type: 'object',
properties: {
- ignoreArrowShorthand: { type: 'boolean' },
- ignoreVoidOperator: { type: 'boolean' },
+ ignoreArrowShorthand: {
+ description: 'Whether to ignore "shorthand" `() =>` arrow functions: those without `{ ... }` braces.',
+ type: 'boolean',
+ },
+ ignoreVoidOperator: {
+ description: 'Whether to ignore returns that start with the `void` operator.',
+ type: 'boolean',
+ },
},
additionalProperties: false,
diff --git a/dist/rules/no-deprecated.js b/dist/rules/no-deprecated.js
index v8.3.0..v8.8.0 100644
--- a/dist/rules/no-deprecated.js
+++ b/dist/rules/no-deprecated.js
@@ -51,4 +51,35 @@
const services = (0, util_1.getParserServices)(context);
const checker = services.program.getTypeChecker();
+ // Deprecated jsdoc tags can be added on some symbol alias, e.g.
+ //
+ // export { /** @deprecated */ foo }
+ //
+ // When we import foo, its symbol is an alias of the exported foo (the one
+ // with the deprecated tag), which is itself an alias of the original foo.
+ // Therefore, we carefully go through the chain of aliases and check each
+ // immediate alias for deprecated tags
+ function searchForDeprecationInAliasesChain(symbol, checkDeprecationsOfAliasedSymbol) {
+ if (!symbol || !tsutils.isSymbolFlagSet(symbol, ts.SymbolFlags.Alias)) {
+ return checkDeprecationsOfAliasedSymbol
+ ? getJsDocDeprecation(symbol)
+ : undefined;
+ }
+ const targetSymbol = checker.getAliasedSymbol(symbol);
+ while (tsutils.isSymbolFlagSet(symbol, ts.SymbolFlags.Alias)) {
+ const reason = getJsDocDeprecation(symbol);
+ if (reason !== undefined) {
+ return reason;
+ }
+ const immediateAliasedSymbol = symbol.getDeclarations() && checker.getImmediateAliasedSymbol(symbol);
+ if (!immediateAliasedSymbol) {
+ break;
+ }
+ symbol = immediateAliasedSymbol;
+ if (checkDeprecationsOfAliasedSymbol && symbol === targetSymbol) {
+ return getJsDocDeprecation(symbol);
+ }
+ }
+ return undefined;
+ }
function isDeclaration(node) {
const { parent } = node;
@@ -65,10 +96,12 @@
return parent.key === node;
case utils_1.AST_NODE_TYPES.Property:
+ // foo in "const { foo } = bar" will be processed twice, as parent.key
+ // and parent.value. The second is treated as a declaration.
return ((parent.shorthand && parent.value === node) ||
parent.parent.type === utils_1.AST_NODE_TYPES.ObjectExpression);
case utils_1.AST_NODE_TYPES.AssignmentPattern:
- return (parent.left === node &&
- !(parent.parent.type === utils_1.AST_NODE_TYPES.Property &&
- parent.parent.shorthand));
+ // foo in "const { foo = "" } = bar" will be processed twice, as parent.parent.key
+ // and parent.left. The second is treated as a declaration.
+ return parent.left === node;
case utils_1.AST_NODE_TYPES.ArrowFunctionExpression:
case utils_1.AST_NODE_TYPES.FunctionDeclaration:
@@ -115,7 +148,13 @@
}
function getJsDocDeprecation(symbol) {
- const tag = symbol
- ?.getJsDocTags(checker)
- .find(tag => tag.name === 'deprecated');
+ let jsDocTags;
+ try {
+ jsDocTags = symbol?.getJsDocTags(checker);
+ }
+ catch {
+ // workaround for https://github.com/microsoft/TypeScript/issues/60024
+ return;
+ }
+ const tag = jsDocTags?.find(tag => tag.name === 'deprecated');
if (!tag) {
return undefined;
@@ -148,33 +187,61 @@
const tsNode = services.esTreeNodeToTSNodeMap.get(node.parent);
// If the node is a direct function call, we look for its signature.
- const signature = checker.getResolvedSignature(tsNode);
- if (signature) {
- const signatureDeprecation = getJsDocDeprecation(signature);
- if (signatureDeprecation !== undefined) {
- return signatureDeprecation;
- }
- }
- // Or it could be a ClassDeclaration or a variable set to a ClassExpression.
+ const signature = (0, util_1.nullThrows)(checker.getResolvedSignature(tsNode), 'Expected call like node to have signature');
const symbol = services.getSymbolAtLocation(node);
- const symbolAtLocation = symbol && checker.getTypeOfSymbolAtLocation(symbol, tsNode).getSymbol();
- return symbolAtLocation &&
- tsutils.isSymbolFlagSet(symbolAtLocation, ts.SymbolFlags.Class)
- ? getJsDocDeprecation(symbolAtLocation)
- : undefined;
- }
- function getSymbol(node) {
- if (node.parent.type === utils_1.AST_NODE_TYPES.AssignmentPattern ||
- node.parent.type === utils_1.AST_NODE_TYPES.Property) {
- return services
- .getTypeAtLocation(node.parent.parent)
- .getProperty(node.name);
+ const aliasedSymbol = symbol !== undefined &&
+ tsutils.isSymbolFlagSet(symbol, ts.SymbolFlags.Alias)
+ ? checker.getAliasedSymbol(symbol)
+ : symbol;
+ const symbolDeclarationKind = aliasedSymbol?.declarations?.[0].kind;
+ // Properties with function-like types have "deprecated" jsdoc
+ // on their symbols, not on their signatures:
+ //
+ // interface Props {
+ // /** @deprecated */
+ // property: () => 'foo'
+ // ^symbol^ ^signature^
+ // }
+ if (symbolDeclarationKind !== ts.SyntaxKind.MethodDeclaration &&
+ symbolDeclarationKind !== ts.SyntaxKind.FunctionDeclaration &&
+ symbolDeclarationKind !== ts.SyntaxKind.MethodSignature) {
+ return (searchForDeprecationInAliasesChain(symbol, true) ??
+ getJsDocDeprecation(signature) ??
+ getJsDocDeprecation(aliasedSymbol));
}
- return services.getSymbolAtLocation(node);
+ return (searchForDeprecationInAliasesChain(symbol,
+ // Here we're working with a function declaration or method.
+ // Both can have 1 or more overloads, each overload creates one
+ // ts.Declaration which is placed in symbol.declarations.
+ //
+ // Imagine the following code:
+ //
+ // function foo(): void
+ // /** @deprecated Some Reason */
+ // function foo(arg: string): void
+ // function foo(arg?: string): void {}
+ //
+ // foo() // <- foo is our symbol
+ //
+ // If we call getJsDocDeprecation(checker.getAliasedSymbol(symbol)),
+ // we get 'Some Reason', but after all, we are calling foo with
+ // a signature that is not deprecated!
+ // It works this way because symbol.getJsDocTags returns tags from
+ // all symbol declarations combined into one array. And AFAIK there is
+ // no publicly exported TS function that can tell us if a particular
+ // declaration is deprecated or not.
+ //
+ // So, in case of function and method declarations, we don't check original
+ // aliased symbol, but rely on the getJsDocDeprecation(signature) call below.
+ false) ?? getJsDocDeprecation(signature));
}
function getDeprecationReason(node) {
const callLikeNode = getCallLikeNode(node);
- return callLikeNode
- ? getCallLikeDeprecation(callLikeNode)
- : getJsDocDeprecation(getSymbol(node));
+ if (callLikeNode) {
+ return getCallLikeDeprecation(callLikeNode);
+ }
+ if (node.parent.type === utils_1.AST_NODE_TYPES.Property) {
+ return getJsDocDeprecation(services.getTypeAtLocation(node.parent.parent).getProperty(node.name));
+ }
+ return searchForDeprecationInAliasesChain(services.getSymbolAtLocation(node), true);
}
function checkIdentifier(node) {
diff --git a/dist/rules/no-duplicate-type-constituents.js b/dist/rules/no-duplicate-type-constituents.js
index v8.3.0..v8.8.0 100644
--- a/dist/rules/no-duplicate-type-constituents.js
+++ b/dist/rules/no-duplicate-type-constituents.js
@@ -26,4 +26,5 @@
const utils_1 = require("@typescript-eslint/utils");
const tsutils = __importStar(require("ts-api-utils"));
+const ts = __importStar(require("typescript"));
const util_1 = require("../util");
const astIgnoreKeys = new Set(['range', 'loc', 'parent']);
@@ -69,4 +70,5 @@
messages: {
duplicate: '{{type}} type constituent is duplicated with {{previous}}.',
+ unnecessary: 'Explicit undefined is unnecessary on an optional parameter.',
},
schema: [
@@ -76,7 +78,9 @@
properties: {
ignoreIntersections: {
+ description: 'Whether to ignore `&` intersections.',
type: 'boolean',
},
ignoreUnions: {
+ description: 'Whether to ignore `|` unions.',
type: 'boolean',
},
@@ -93,5 +97,6 @@
create(context, [{ ignoreIntersections, ignoreUnions }]) {
const parserServices = (0, util_1.getParserServices)(context);
- function checkDuplicate(node) {
+ const { sourceCode } = context;
+ function checkDuplicate(node, forEachNodeType) {
const cachedTypeMap = new Map();
node.types.reduce((uniqueConstituents, constituentNode) => {
@@ -100,55 +105,57 @@
return uniqueConstituents;
}
- const duplicatedPreviousConstituentInAst = uniqueConstituents.find(ele => isSameAstNode(ele, constituentNode));
- if (duplicatedPreviousConstituentInAst) {
- reportDuplicate({
- duplicated: constituentNode,
- duplicatePrevious: duplicatedPreviousConstituentInAst,
- }, node);
+ const report = (messageId, data) => {
+ const getUnionOrIntersectionToken = (where, at) => sourceCode[`getTokens${where}`](constituentNode, {
+ filter: token => ['|', '&'].includes(token.value),
+ }).at(at);
+ const beforeUnionOrIntersectionToken = getUnionOrIntersectionToken('Before', -1);
+ let afterUnionOrIntersectionToken;
+ let bracketBeforeTokens;
+ let bracketAfterTokens;
+ if (beforeUnionOrIntersectionToken) {
+ bracketBeforeTokens = sourceCode.getTokensBetween(beforeUnionOrIntersectionToken, constituentNode);
+ bracketAfterTokens = sourceCode.getTokensAfter(constituentNode, {
+ count: bracketBeforeTokens.length,
+ });
+ }
+ else {
+ afterUnionOrIntersectionToken = (0, util_1.nullThrows)(getUnionOrIntersectionToken('After', 0), util_1.NullThrowsReasons.MissingToken('union or intersection token', 'duplicate type constituent'));
+ bracketAfterTokens = sourceCode.getTokensBetween(constituentNode, afterUnionOrIntersectionToken);
+ bracketBeforeTokens = sourceCode.getTokensBefore(constituentNode, {
+ count: bracketAfterTokens.length,
+ });
+ }
+ context.report({
+ data,
+ messageId,
+ node: constituentNode,
+ loc: {
+ start: constituentNode.loc.start,
+ end: (bracketAfterTokens.at(-1) ?? constituentNode).loc.end,
+ },
+ fix: fixer => [
+ beforeUnionOrIntersectionToken,
+ ...bracketBeforeTokens,
+ constituentNode,
+ ...bracketAfterTokens,
+ afterUnionOrIntersectionToken,
+ ].flatMap(token => (token ? fixer.remove(token) : [])),
+ });
+ };
+ const duplicatePrevious = uniqueConstituents.find(ele => isSameAstNode(ele, constituentNode)) ?? cachedTypeMap.get(constituentNodeType);
+ if (duplicatePrevious) {
+ report('duplicate', {
+ type: node.type === utils_1.AST_NODE_TYPES.TSIntersectionType
+ ? 'Intersection'
+ : 'Union',
+ previous: sourceCode.getText(duplicatePrevious),
+ });
return uniqueConstituents;
}
- const duplicatedPreviousConstituentInType = cachedTypeMap.get(constituentNodeType);
- if (duplicatedPreviousConstituentInType) {
- reportDuplicate({
- duplicated: constituentNode,
- duplicatePrevious: duplicatedPreviousConstituentInType,
- }, node);
- return uniqueConstituents;
- }
+ forEachNodeType?.(constituentNodeType, report);
cachedTypeMap.set(constituentNodeType, constituentNode);
return [...uniqueConstituents, constituentNode];
}, []);
}
- function reportDuplicate(duplicateConstituent, parentNode) {
- const beforeTokens = context.sourceCode.getTokensBefore(duplicateConstituent.duplicated, { filter: token => token.value === '|' || token.value === '&' });
- const beforeUnionOrIntersectionToken = beforeTokens[beforeTokens.length - 1];
- const bracketBeforeTokens = context.sourceCode.getTokensBetween(beforeUnionOrIntersectionToken, duplicateConstituent.duplicated);
- const bracketAfterTokens = context.sourceCode.getTokensAfter(duplicateConstituent.duplicated, { count: bracketBeforeTokens.length });
- const reportLocation = {
- start: duplicateConstituent.duplicated.loc.start,
- end: bracketAfterTokens.length > 0
- ? bracketAfterTokens[bracketAfterTokens.length - 1].loc.end
- : duplicateConstituent.duplicated.loc.end,
- };
- context.report({
- data: {
- type: parentNode.type === utils_1.AST_NODE_TYPES.TSIntersectionType
- ? 'Intersection'
- : 'Union',
- previous: context.sourceCode.getText(duplicateConstituent.duplicatePrevious),
- },
- messageId: 'duplicate',
- node: duplicateConstituent.duplicated,
- loc: reportLocation,
- fix: fixer => {
- return [
- beforeUnionOrIntersectionToken,
- ...bracketBeforeTokens,
- duplicateConstituent.duplicated,
- ...bracketAfterTokens,
- ].map(token => fixer.remove(token));
- },
- });
- }
return {
...(!ignoreIntersections && {
@@ -156,5 +163,19 @@
}),
...(!ignoreUnions && {
- TSUnionType: checkDuplicate,
+ TSUnionType: (node) => checkDuplicate(node, (constituentNodeType, report) => {
+ const maybeTypeAnnotation = node.parent;
+ if (maybeTypeAnnotation.type === utils_1.AST_NODE_TYPES.TSTypeAnnotation) {
+ const maybeIdentifier = maybeTypeAnnotation.parent;
+ if (maybeIdentifier.type === utils_1.AST_NODE_TYPES.Identifier &&
+ maybeIdentifier.optional) {
+ const maybeFunction = maybeIdentifier.parent;
+ if ((0, util_1.isFunctionOrFunctionType)(maybeFunction) &&
+ maybeFunction.params.includes(maybeIdentifier) &&
+ tsutils.isTypeFlagSet(constituentNodeType, ts.TypeFlags.Undefined)) {
+ report('unnecessary');
+ }
+ }
+ }
+ }),
}),
};
diff --git a/dist/rules/no-empty-function.js b/dist/rules/no-empty-function.js
index v8.3.0..v8.8.0 100644
--- a/dist/rules/no-empty-function.js
+++ b/dist/rules/no-empty-function.js
@@ -12,4 +12,5 @@
properties: {
allow: {
+ description: 'Locations and kinds of functions that are allowed to be empty.',
items: {
type: 'string',
diff --git a/dist/rules/no-empty-interface.js b/dist/rules/no-empty-interface.js
index v8.3.0..v8.8.0 100644
--- a/dist/rules/no-empty-interface.js
+++ b/dist/rules/no-empty-interface.js
@@ -25,4 +25,5 @@
properties: {
allowSingleExtends: {
+ description: 'Whether to allow empty interfaces that extend a single other interface.',
type: 'boolean',
},
diff --git a/dist/rules/no-empty-object-type.js b/dist/rules/no-empty-object-type.js
index v8.3.0..v8.8.0 100644
--- a/dist/rules/no-empty-object-type.js
+++ b/dist/rules/no-empty-object-type.js
@@ -32,12 +32,15 @@
properties: {
allowInterfaces: {
+ description: 'Whether to allow empty interfaces.',
enum: ['always', 'never', 'with-single-extends'],
type: 'string',
},
allowObjectTypes: {
+ description: 'Whether to allow empty object type literals.',
enum: ['always', 'never'],
type: 'string',
},
allowWithName: {
+ description: 'A stringified regular expression to allow interfaces and object type aliases with the configured name.',
type: 'string',
},
diff --git a/dist/rules/no-floating-promises.js b/dist/rules/no-floating-promises.js
index v8.3.0..v8.8.0 100644
--- a/dist/rules/no-floating-promises.js
+++ b/dist/rules/no-floating-promises.js
@@ -58,6 +58,12 @@
type: 'object',
properties: {
- allowForKnownSafePromises: util_1.readonlynessOptionsSchema.properties.allow,
- allowForKnownSafeCalls: util_1.readonlynessOptionsSchema.properties.allow,
+ allowForKnownSafePromises: {
+ ...util_1.readonlynessOptionsSchema.properties.allow,
+ description: 'Type specifiers that are known to be safe to float.',
+ },
+ allowForKnownSafeCalls: {
+ ...util_1.readonlynessOptionsSchema.properties.allow,
+ description: 'Type specifiers of functions whose calls are safe to float.',
+ },
checkThenables: {
description: 'Whether to check all "Thenable"s, not just the built-in Promise type.',
@@ -181,5 +187,5 @@
}
const type = services.getTypeAtLocation(node.callee);
- return allowForKnownSafeCalls.some(allowedType => (0, util_1.typeMatchesSpecifier)(type, allowedType, services.program));
+ return (0, util_1.typeMatchesSomeSpecifier)(type, allowForKnownSafeCalls, services.program);
}
function isHigherPrecedenceThanUnary(node) {
@@ -244,24 +250,32 @@
// If the outer expression is a call, a `.catch()` or `.then()` with
// rejection handler handles the promise.
- const catchRejectionHandler = getRejectionHandlerFromCatchCall(node);
- if (catchRejectionHandler) {
- if (isValidRejectionHandler(catchRejectionHandler)) {
- return { isUnhandled: false };
+ const { callee } = node;
+ if (callee.type === utils_1.AST_NODE_TYPES.MemberExpression) {
+ const methodName = (0, util_1.getStaticMemberAccessValue)(callee, context);
+ const catchRejectionHandler = methodName === 'catch' && node.arguments.length >= 1
+ ? node.arguments[0]
+ : undefined;
+ if (catchRejectionHandler) {
+ if (isValidRejectionHandler(catchRejectionHandler)) {
+ return { isUnhandled: false };
+ }
+ return { isUnhandled: true, nonFunctionHandler: true };
}
- return { isUnhandled: true, nonFunctionHandler: true };
- }
- const thenRejectionHandler = getRejectionHandlerFromThenCall(node);
- if (thenRejectionHandler) {
- if (isValidRejectionHandler(thenRejectionHandler)) {
- return { isUnhandled: false };
+ const thenRejectionHandler = methodName === 'then' && node.arguments.length >= 2
+ ? node.arguments[1]
+ : undefined;
+ if (thenRejectionHandler) {
+ if (isValidRejectionHandler(thenRejectionHandler)) {
+ return { isUnhandled: false };
+ }
+ return { isUnhandled: true, nonFunctionHandler: true };
}
- return { isUnhandled: true, nonFunctionHandler: true };
+ // `x.finally()` is transparent to resolution of the promise, so check `x`.
+ // ("object" in this context is the `x` in `x.finally()`)
+ const promiseFinallyObject = methodName === 'finally' ? callee.object : undefined;
+ if (promiseFinallyObject) {
+ return isUnhandledPromise(checker, promiseFinallyObject);
+ }
}
- // `x.finally()` is transparent to resolution of the promise, so check `x`.
- // ("object" in this context is the `x` in `x.finally()`)
- const promi (too long so truncated)
Command detailsnpm diff --diff=@typescript-eslint/eslint-plugin@8.3.0 --diff=@typescript-eslint/eslint-plugin@8.8.0 --diff-unified=2 See also the Reported by ybiquitous/npm-diff-action@v1.6.0 (Node.js 22.9.0 and npm 10.8.3) |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Labels
dependencies
Pull requests that update a dependency file
javascript
Pull requests that update Javascript code
0 participants
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Bumps @typescript-eslint/eslint-plugin from 8.3.0 to 8.8.0.
Release notes
Sourced from
@typescript-eslint/eslint-plugin
's releases.... (truncated)
Changelog
Sourced from
@typescript-eslint/eslint-plugin
's changelog.... (truncated)
Commits
2055cfb
chore(release): publish 8.8.0b121bd9
feat(eslint-plugin): [return-await] check for-await loop iteree (#10008)9028d9d
docs: [no-unsafe-enum-comparison] clarify motivation and applicability (#10029)977e0a1
fix(eslint-plugin): [prefer-literal-enum-member] allow nested bitwise operati...6ce66b5
fix: removeexport type *
in d.ts to support TS<5.0 (#10070)a916ff2
feat(eslint-plugin): [no-unnecessary-condition] add checkTypePredicates (#10009)b36d524
fix(eslint-plugin): [no-misused-promises] check contextual type (#10042)7a216fe
fix(eslint-plugin): [no-deprecated] max callstack exceeded when class impleme...94c5484
fix(type-utils): check for type parameters onisBuiltinSymbolLikeRecurser()
...ced951b
docs: [prefer-literal-enum-member] fix bad examples (#10035)Dependabot will resolve any conflicts with this PR as long as you don't alter it yourself. You can also trigger a rebase manually by commenting
@dependabot rebase
.Dependabot commands and options
You can trigger Dependabot actions by commenting on this PR:
@dependabot rebase
will rebase this PR@dependabot recreate
will recreate this PR, overwriting any edits that have been made to it@dependabot merge
will merge this PR after your CI passes on it@dependabot squash and merge
will squash and merge this PR after your CI passes on it@dependabot cancel merge
will cancel a previously requested merge and block automerging@dependabot reopen
will reopen this PR if it is closed@dependabot close
will close this PR and stop Dependabot recreating it. You can achieve the same result by closing it manually@dependabot show <dependency name> ignore conditions
will show all of the ignore conditions of the specified dependency@dependabot ignore this major version
will close this PR and stop Dependabot creating any more for this major version (unless you reopen the PR or upgrade to it yourself)@dependabot ignore this minor version
will close this PR and stop Dependabot creating any more for this minor version (unless you reopen the PR or upgrade to it yourself)@dependabot ignore this dependency
will close this PR and stop Dependabot creating any more for this dependency (unless you reopen the PR or upgrade to it yourself)