@typescript-eslint/eslint-plugin 8.62.2-alpha.9 → 8.63.1-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.
@@ -1,5 +1,5 @@
1
1
  export type Options = [('method' | 'property')?];
2
- export type MessageIds = 'errorMethod' | 'errorProperty';
2
+ export type MessageIds = 'convertToMethodSignature' | 'errorMethod' | 'errorProperty';
3
3
  declare const _default: import("@typescript-eslint/utils/ts-eslint").RuleModule<MessageIds, Options, import("../../rules").ESLintPluginDocs, import("@typescript-eslint/utils/ts-eslint").RuleListener> & {
4
4
  name: string;
5
5
  };
@@ -10,7 +10,9 @@ exports.default = (0, util_1.createRule)({
10
10
  description: 'Enforce using a particular method signature syntax',
11
11
  },
12
12
  fixable: 'code',
13
+ hasSuggestions: true,
13
14
  messages: {
15
+ convertToMethodSignature: 'Convert to a method signature. This removes the `readonly` modifier, allowing the member to be reassigned.',
14
16
  errorMethod: 'Shorthand method signature is forbidden. Use a function property instead.',
15
17
  errorProperty: 'Function property signature is forbidden. Use a method shorthand instead.',
16
18
  },
@@ -32,9 +34,6 @@ exports.default = (0, util_1.createRule)({
32
34
  if (node.optional) {
33
35
  key = `${key}?`;
34
36
  }
35
- if (node.readonly) {
36
- key = `readonly ${key}`;
37
- }
38
37
  return key;
39
38
  }
40
39
  function getMethodParams(node) {
@@ -164,16 +163,30 @@ exports.default = (0, util_1.createRule)({
164
163
  if (typeNode?.type !== utils_1.AST_NODE_TYPES.TSFunctionType) {
165
164
  return;
166
165
  }
166
+ const fix = fixer => {
167
+ const key = getMethodKey(propertyNode);
168
+ const params = getMethodParams(typeNode);
169
+ const returnType = getMethodReturnType(typeNode);
170
+ const delimiter = getDelimiter(propertyNode);
171
+ return fixer.replaceText(propertyNode, `${key}${params}: ${returnType}${delimiter}`);
172
+ };
173
+ // There is no syntax for a `readonly` method signature, so converting
174
+ // a `readonly` function-typed property drops the `readonly` modifier.
175
+ // That is a behavioral change (a method may be reassigned, a
176
+ // `readonly` property may not), so it is offered as a suggestion
177
+ // rather than applied as an autofix.
178
+ if (propertyNode.readonly) {
179
+ context.report({
180
+ node: propertyNode,
181
+ messageId: 'errorProperty',
182
+ suggest: [{ messageId: 'convertToMethodSignature', fix }],
183
+ });
184
+ return;
185
+ }
167
186
  context.report({
168
187
  node: propertyNode,
169
188
  messageId: 'errorProperty',
170
- fix: fixer => {
171
- const key = getMethodKey(propertyNode);
172
- const params = getMethodParams(typeNode);
173
- const returnType = getMethodReturnType(typeNode);
174
- const delimiter = getDelimiter(propertyNode);
175
- return fixer.replaceText(propertyNode, `${key}${params}: ${returnType}${delimiter}`);
176
- },
189
+ fix,
177
190
  });
178
191
  },
179
192
  }),
@@ -234,7 +234,7 @@ exports.default = (0, util_1.createRule)({
234
234
  node.arguments[0]) {
235
235
  const scope = context.sourceCode.getScope(node);
236
236
  // eslint-disable-next-line @typescript-eslint/internal/prefer-ast-types-enum
237
- const variable = scope.set.get('String');
237
+ const variable = utils_1.ASTUtils.findVariable(scope, 'String');
238
238
  return !variable?.defs.length;
239
239
  }
240
240
  return false;
@@ -340,11 +340,27 @@ exports.default = (0, util_1.createRule)({
340
340
  }
341
341
  function checkVariableDeclaration(node) {
342
342
  const tsNode = services.esTreeNodeToTSNodeMap.get(node);
343
- if (tsNode.initializer == null ||
344
- node.init == null ||
345
- node.id.typeAnnotation == null) {
343
+ if (tsNode.initializer == null || node.init == null) {
346
344
  return;
347
345
  }
346
+ if (node.parent.kind === 'using' &&
347
+ hasWellKnownSymbolWithThenableReturn(checker, tsNode.initializer, checker.getTypeAtLocation(tsNode.initializer), 'dispose')) {
348
+ context.report({
349
+ node: node.init,
350
+ messageId: 'voidReturnVariable',
351
+ });
352
+ }
353
+ if (node.id.typeAnnotation == null) {
354
+ return;
355
+ }
356
+ const variableType = services.getTypeAtLocation(node.id);
357
+ if (hasWellKnownSymbolWithVoidReturn(checker, tsNode.name, variableType, 'dispose') &&
358
+ hasWellKnownSymbolWithThenableReturn(checker, tsNode.initializer, checker.getTypeAtLocation(tsNode.initializer), 'dispose')) {
359
+ context.report({
360
+ node: node.init,
361
+ messageId: 'voidReturnVariable',
362
+ });
363
+ }
348
364
  // syntactically ignore some known-good cases to avoid touching type info
349
365
  if (!isPossiblyFunctionType(node.id.typeAnnotation)) {
350
366
  return;
@@ -763,3 +779,25 @@ function isStaticMember(node) {
763
779
  node.type === utils_1.AST_NODE_TYPES.AccessorProperty) &&
764
780
  node.static);
765
781
  }
782
+ function hasWellKnownSymbolWithThenableReturn(checker, node, type, symbolName) {
783
+ return tsutils
784
+ .unionConstituents(checker.getApparentType(type))
785
+ .some(typePart => {
786
+ const symbol = tsutils.getWellKnownSymbolPropertyOfType(typePart, symbolName, checker);
787
+ if (symbol == null) {
788
+ return false;
789
+ }
790
+ return isThenableReturningFunctionType(checker, node, checker.getTypeOfSymbolAtLocation(symbol, node));
791
+ });
792
+ }
793
+ function hasWellKnownSymbolWithVoidReturn(checker, node, type, symbolName) {
794
+ return tsutils
795
+ .unionConstituents(checker.getApparentType(type))
796
+ .some(typePart => {
797
+ const symbol = tsutils.getWellKnownSymbolPropertyOfType(typePart, symbolName, checker);
798
+ if (symbol == null) {
799
+ return false;
800
+ }
801
+ return isVoidReturningFunctionType(checker, node, checker.getTypeOfSymbolAtLocation(symbol, node));
802
+ });
803
+ }
@@ -414,7 +414,11 @@ exports.default = (0, util_1.createRule)({
414
414
  !parent.arguments.includes(node)) {
415
415
  return false;
416
416
  }
417
- const calleeType = checker.getTypeAtLocation(services.esTreeNodeToTSNodeMap.get(parent.callee));
417
+ // An optional-chained callee (`foo?.bar(...)`) types as `<method> | undefined`,
418
+ // and a union exposes no call signatures — strip the nullability first.
419
+ const calleeType = services
420
+ .getTypeAtLocation(parent.callee)
421
+ .getNonNullableType();
418
422
  const signatures = calleeType.getCallSignatures();
419
423
  if (signatures.length <= 1) {
420
424
  return false;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@typescript-eslint/eslint-plugin",
3
- "version": "8.62.2-alpha.9",
3
+ "version": "8.63.1-alpha.0",
4
4
  "description": "TypeScript plugin for ESLint",
5
5
  "files": [
6
6
  "dist",
@@ -49,10 +49,10 @@
49
49
  "ignore": "^7.0.5",
50
50
  "natural-compare": "^1.4.0",
51
51
  "ts-api-utils": "^2.5.0",
52
- "@typescript-eslint/scope-manager": "8.62.2-alpha.9",
53
- "@typescript-eslint/type-utils": "8.62.2-alpha.9",
54
- "@typescript-eslint/utils": "8.62.2-alpha.9",
55
- "@typescript-eslint/visitor-keys": "8.62.2-alpha.9"
52
+ "@typescript-eslint/scope-manager": "8.63.1-alpha.0",
53
+ "@typescript-eslint/type-utils": "8.63.1-alpha.0",
54
+ "@typescript-eslint/utils": "8.63.1-alpha.0",
55
+ "@typescript-eslint/visitor-keys": "8.63.1-alpha.0"
56
56
  },
57
57
  "devDependencies": {
58
58
  "@types/json-schema": "^7.0.15",
@@ -76,13 +76,13 @@
76
76
  "typescript": ">=4.8.4 <6.1.0",
77
77
  "unist-util-visit": "^5.0.0",
78
78
  "vitest": "^4.0.18",
79
- "@typescript-eslint/rule-tester": "8.62.2-alpha.9",
80
- "@typescript-eslint/rule-schema-to-typescript-types": "8.62.2-alpha.9"
79
+ "@typescript-eslint/rule-tester": "8.63.1-alpha.0",
80
+ "@typescript-eslint/rule-schema-to-typescript-types": "8.63.1-alpha.0"
81
81
  },
82
82
  "peerDependencies": {
83
83
  "eslint": "^8.57.0 || ^9.0.0 || ^10.0.0",
84
84
  "typescript": ">=4.8.4 <6.1.0",
85
- "@typescript-eslint/parser": "^8.62.2-alpha.9"
85
+ "@typescript-eslint/parser": "^8.63.1-alpha.0"
86
86
  },
87
87
  "funding": {
88
88
  "type": "opencollective",