@typescript-eslint/eslint-plugin 8.52.1-alpha.12 → 8.52.1-alpha.13

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.
@@ -148,6 +148,7 @@ declare const _default: {
148
148
  'no-return-await': "off";
149
149
  '@typescript-eslint/return-await': "error";
150
150
  '@typescript-eslint/strict-boolean-expressions': "error";
151
+ '@typescript-eslint/strict-void-return': "error";
151
152
  '@typescript-eslint/switch-exhaustiveness-check': "error";
152
153
  '@typescript-eslint/triple-slash-reference': "error";
153
154
  '@typescript-eslint/unbound-method': "error";
@@ -155,6 +155,7 @@ module.exports = {
155
155
  'no-return-await': 'off',
156
156
  '@typescript-eslint/return-await': 'error',
157
157
  '@typescript-eslint/strict-boolean-expressions': 'error',
158
+ '@typescript-eslint/strict-void-return': 'error',
158
159
  '@typescript-eslint/switch-exhaustiveness-check': 'error',
159
160
  '@typescript-eslint/triple-slash-reference': 'error',
160
161
  '@typescript-eslint/unbound-method': 'error',
@@ -62,6 +62,7 @@ declare const _default: {
62
62
  '@typescript-eslint/restrict-template-expressions': "off";
63
63
  '@typescript-eslint/return-await': "off";
64
64
  '@typescript-eslint/strict-boolean-expressions': "off";
65
+ '@typescript-eslint/strict-void-return': "off";
65
66
  '@typescript-eslint/switch-exhaustiveness-check': "off";
66
67
  '@typescript-eslint/unbound-method': "off";
67
68
  '@typescript-eslint/use-unknown-in-catch-callback-variable': "off";
@@ -65,6 +65,7 @@ module.exports = {
65
65
  '@typescript-eslint/restrict-template-expressions': 'off',
66
66
  '@typescript-eslint/return-await': 'off',
67
67
  '@typescript-eslint/strict-boolean-expressions': 'off',
68
+ '@typescript-eslint/strict-void-return': 'off',
68
69
  '@typescript-eslint/switch-exhaustiveness-check': 'off',
69
70
  '@typescript-eslint/unbound-method': 'off',
70
71
  '@typescript-eslint/use-unknown-in-catch-callback-variable': 'off',
@@ -168,6 +168,7 @@ exports.default = (plugin, parser) => [
168
168
  'no-return-await': 'off',
169
169
  '@typescript-eslint/return-await': 'error',
170
170
  '@typescript-eslint/strict-boolean-expressions': 'error',
171
+ '@typescript-eslint/strict-void-return': 'error',
171
172
  '@typescript-eslint/switch-exhaustiveness-check': 'error',
172
173
  '@typescript-eslint/triple-slash-reference': 'error',
173
174
  '@typescript-eslint/unbound-method': 'error',
@@ -70,6 +70,7 @@ exports.default = (_plugin, _parser) => ({
70
70
  '@typescript-eslint/restrict-template-expressions': 'off',
71
71
  '@typescript-eslint/return-await': 'off',
72
72
  '@typescript-eslint/strict-boolean-expressions': 'off',
73
+ '@typescript-eslint/strict-void-return': 'off',
73
74
  '@typescript-eslint/switch-exhaustiveness-check': 'off',
74
75
  '@typescript-eslint/unbound-method': 'off',
75
76
  '@typescript-eslint/use-unknown-in-catch-callback-variable': 'off',
package/dist/index.d.ts CHANGED
@@ -150,6 +150,7 @@ declare const _default: {
150
150
  'no-return-await': "off";
151
151
  '@typescript-eslint/return-await': "error";
152
152
  '@typescript-eslint/strict-boolean-expressions': "error";
153
+ '@typescript-eslint/strict-void-return': "error";
153
154
  '@typescript-eslint/switch-exhaustiveness-check': "error";
154
155
  '@typescript-eslint/triple-slash-reference': "error";
155
156
  '@typescript-eslint/unbound-method': "error";
@@ -228,6 +229,7 @@ declare const _default: {
228
229
  '@typescript-eslint/restrict-template-expressions': "off";
229
230
  '@typescript-eslint/return-await': "off";
230
231
  '@typescript-eslint/strict-boolean-expressions': "off";
232
+ '@typescript-eslint/strict-void-return': "off";
231
233
  '@typescript-eslint/switch-exhaustiveness-check': "off";
232
234
  '@typescript-eslint/unbound-method': "off";
233
235
  '@typescript-eslint/use-unknown-in-catch-callback-variable': "off";
@@ -1096,6 +1098,11 @@ declare const _default: {
1096
1098
  'strict-boolean-expressions': import("@typescript-eslint/utils/ts-eslint").RuleModule<import("./rules/strict-boolean-expressions").MessageId, import("./rules/strict-boolean-expressions").Options, import("../rules").ESLintPluginDocs, import("@typescript-eslint/utils/ts-eslint").RuleListener> & {
1097
1099
  name: string;
1098
1100
  };
1101
+ 'strict-void-return': import("@typescript-eslint/utils/ts-eslint").RuleModule<"asyncFunc" | "nonVoidFunc" | "nonVoidReturn", [{
1102
+ allowReturnAny?: boolean;
1103
+ }], import("../rules").ESLintPluginDocs, import("@typescript-eslint/utils/ts-eslint").RuleListener> & {
1104
+ name: string;
1105
+ };
1099
1106
  'switch-exhaustiveness-check': import("@typescript-eslint/utils/ts-eslint").RuleModule<import("./rules/switch-exhaustiveness-check").MessageIds, import("./rules/switch-exhaustiveness-check").Options, import("../rules").ESLintPluginDocs, import("@typescript-eslint/utils/ts-eslint").RuleListener> & {
1100
1107
  name: string;
1101
1108
  };
@@ -172,6 +172,7 @@ declare const _default: {
172
172
  'no-return-await': "off";
173
173
  '@typescript-eslint/return-await': "error";
174
174
  '@typescript-eslint/strict-boolean-expressions': "error";
175
+ '@typescript-eslint/strict-void-return': "error";
175
176
  '@typescript-eslint/switch-exhaustiveness-check': "error";
176
177
  '@typescript-eslint/triple-slash-reference': "error";
177
178
  '@typescript-eslint/unbound-method': "error";
@@ -250,6 +251,7 @@ declare const _default: {
250
251
  '@typescript-eslint/restrict-template-expressions': "off";
251
252
  '@typescript-eslint/return-await': "off";
252
253
  '@typescript-eslint/strict-boolean-expressions': "off";
254
+ '@typescript-eslint/strict-void-return': "off";
253
255
  '@typescript-eslint/switch-exhaustiveness-check': "off";
254
256
  '@typescript-eslint/unbound-method': "off";
255
257
  '@typescript-eslint/use-unknown-in-catch-callback-variable': "off";
@@ -1119,6 +1121,11 @@ declare const _default: {
1119
1121
  'strict-boolean-expressions': TSESLint.RuleModule<import("./rules/strict-boolean-expressions").MessageId, import("./rules/strict-boolean-expressions").Options, import("../rules").ESLintPluginDocs, TSESLint.RuleListener> & {
1120
1122
  name: string;
1121
1123
  };
1124
+ 'strict-void-return': TSESLint.RuleModule<"asyncFunc" | "nonVoidFunc" | "nonVoidReturn", [{
1125
+ allowReturnAny?: boolean;
1126
+ }], import("../rules").ESLintPluginDocs, TSESLint.RuleListener> & {
1127
+ name: string;
1128
+ };
1122
1129
  'switch-exhaustiveness-check': TSESLint.RuleModule<import("./rules/switch-exhaustiveness-check").MessageIds, import("./rules/switch-exhaustiveness-check").Options, import("../rules").ESLintPluginDocs, TSESLint.RuleListener> & {
1123
1130
  name: string;
1124
1131
  };
@@ -419,6 +419,11 @@ declare const rules: {
419
419
  'strict-boolean-expressions': import("@typescript-eslint/utils/ts-eslint").RuleModule<import("./strict-boolean-expressions").MessageId, import("./strict-boolean-expressions").Options, import("../../rules").ESLintPluginDocs, import("@typescript-eslint/utils/ts-eslint").RuleListener> & {
420
420
  name: string;
421
421
  };
422
+ 'strict-void-return': import("@typescript-eslint/utils/ts-eslint").RuleModule<"asyncFunc" | "nonVoidFunc" | "nonVoidReturn", [{
423
+ allowReturnAny?: boolean;
424
+ }], import("../../rules").ESLintPluginDocs, import("@typescript-eslint/utils/ts-eslint").RuleListener> & {
425
+ name: string;
426
+ };
422
427
  'switch-exhaustiveness-check': import("@typescript-eslint/utils/ts-eslint").RuleModule<import("./switch-exhaustiveness-check").MessageIds, import("./switch-exhaustiveness-check").Options, import("../../rules").ESLintPluginDocs, import("@typescript-eslint/utils/ts-eslint").RuleListener> & {
423
428
  name: string;
424
429
  };
@@ -129,6 +129,7 @@ const restrict_template_expressions_1 = __importDefault(require("./restrict-temp
129
129
  const return_await_1 = __importDefault(require("./return-await"));
130
130
  const sort_type_constituents_1 = __importDefault(require("./sort-type-constituents"));
131
131
  const strict_boolean_expressions_1 = __importDefault(require("./strict-boolean-expressions"));
132
+ const strict_void_return_1 = __importDefault(require("./strict-void-return"));
132
133
  const switch_exhaustiveness_check_1 = __importDefault(require("./switch-exhaustiveness-check"));
133
134
  const triple_slash_reference_1 = __importDefault(require("./triple-slash-reference"));
134
135
  const typedef_1 = __importDefault(require("./typedef"));
@@ -263,6 +264,7 @@ const rules = {
263
264
  'return-await': return_await_1.default,
264
265
  'sort-type-constituents': sort_type_constituents_1.default,
265
266
  'strict-boolean-expressions': strict_boolean_expressions_1.default,
267
+ 'strict-void-return': strict_void_return_1.default,
266
268
  'switch-exhaustiveness-check': switch_exhaustiveness_check_1.default,
267
269
  'triple-slash-reference': triple_slash_reference_1.default,
268
270
  typedef: typedef_1.default,
@@ -0,0 +1,10 @@
1
+ type Options = [
2
+ {
3
+ allowReturnAny?: boolean;
4
+ }
5
+ ];
6
+ type MessageId = `asyncFunc` | `nonVoidFunc` | `nonVoidReturn`;
7
+ declare const _default: import("@typescript-eslint/utils/ts-eslint").RuleModule<MessageId, Options, import("../../rules").ESLintPluginDocs, import("@typescript-eslint/utils/ts-eslint").RuleListener> & {
8
+ name: string;
9
+ };
10
+ export default _default;
@@ -0,0 +1,356 @@
1
+ "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
14
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
15
+ }) : function(o, v) {
16
+ o["default"] = v;
17
+ });
18
+ var __importStar = (this && this.__importStar) || (function () {
19
+ var ownKeys = function(o) {
20
+ ownKeys = Object.getOwnPropertyNames || function (o) {
21
+ var ar = [];
22
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
23
+ return ar;
24
+ };
25
+ return ownKeys(o);
26
+ };
27
+ return function (mod) {
28
+ if (mod && mod.__esModule) return mod;
29
+ var result = {};
30
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
31
+ __setModuleDefault(result, mod);
32
+ return result;
33
+ };
34
+ })();
35
+ Object.defineProperty(exports, "__esModule", { value: true });
36
+ const utils_1 = require("@typescript-eslint/utils");
37
+ const tsutils = __importStar(require("ts-api-utils"));
38
+ const ts = __importStar(require("typescript"));
39
+ const util = __importStar(require("../util"));
40
+ exports.default = util.createRule({
41
+ name: 'strict-void-return',
42
+ meta: {
43
+ type: 'problem',
44
+ docs: {
45
+ description: 'Disallow passing a value-returning function in a position accepting a void function',
46
+ requiresTypeChecking: true,
47
+ },
48
+ messages: {
49
+ asyncFunc: 'Async function used in a context where a void function is expected.',
50
+ nonVoidFunc: 'Value-returning function used in a context where a void function is expected.',
51
+ nonVoidReturn: 'Value returned in a context where a void return is expected.',
52
+ },
53
+ schema: [
54
+ {
55
+ type: 'object',
56
+ additionalProperties: false,
57
+ properties: {
58
+ allowReturnAny: {
59
+ type: 'boolean',
60
+ description: 'Whether to allow functions returning `any` to be used in place expecting a `void` function.',
61
+ },
62
+ },
63
+ },
64
+ ],
65
+ },
66
+ defaultOptions: [
67
+ {
68
+ allowReturnAny: false,
69
+ },
70
+ ],
71
+ create(context, [options]) {
72
+ const sourceCode = context.sourceCode;
73
+ const parserServices = util.getParserServices(context);
74
+ const checker = parserServices.program.getTypeChecker();
75
+ return {
76
+ ArrayExpression: (node) => {
77
+ for (const elemNode of node.elements) {
78
+ if (elemNode != null &&
79
+ elemNode.type !== utils_1.AST_NODE_TYPES.SpreadElement) {
80
+ checkExpressionNode(elemNode);
81
+ }
82
+ }
83
+ },
84
+ ArrowFunctionExpression: (node) => {
85
+ if (node.body.type !== utils_1.AST_NODE_TYPES.BlockStatement) {
86
+ checkExpressionNode(node.body);
87
+ }
88
+ },
89
+ AssignmentExpression: (node) => {
90
+ checkExpressionNode(node.right); // should ignore operators like `+=` or `-=` automatically
91
+ },
92
+ 'CallExpression, NewExpression': checkFunctionCallNode,
93
+ JSXAttribute: (node) => {
94
+ if (node.value?.type === utils_1.AST_NODE_TYPES.JSXExpressionContainer &&
95
+ node.value.expression.type !== utils_1.AST_NODE_TYPES.JSXEmptyExpression) {
96
+ checkExpressionNode(node.value.expression);
97
+ }
98
+ },
99
+ MethodDefinition: checkClassMethodNode,
100
+ ObjectExpression: (node) => {
101
+ for (const propNode of node.properties) {
102
+ if (propNode.type !== utils_1.AST_NODE_TYPES.SpreadElement) {
103
+ checkObjectPropertyNode(propNode);
104
+ }
105
+ }
106
+ },
107
+ PropertyDefinition: checkClassPropertyNode,
108
+ ReturnStatement: (node) => {
109
+ if (node.argument != null) {
110
+ checkExpressionNode(node.argument);
111
+ }
112
+ },
113
+ VariableDeclarator: (node) => {
114
+ if (node.init != null) {
115
+ checkExpressionNode(node.init);
116
+ }
117
+ },
118
+ };
119
+ function isVoidReturningFunctionType(type) {
120
+ const returnTypes = tsutils
121
+ .getCallSignaturesOfType(type)
122
+ .flatMap(signature => tsutils.unionConstituents(signature.getReturnType()));
123
+ return (returnTypes.length > 0 &&
124
+ returnTypes.every(type => tsutils.isTypeFlagSet(type, ts.TypeFlags.Void)));
125
+ }
126
+ /**
127
+ * Finds errors in any expression node.
128
+ *
129
+ * Compares the type of the node against the contextual (expected) type.
130
+ *
131
+ * @returns `true` if the expected type was void function.
132
+ */
133
+ function checkExpressionNode(node) {
134
+ const tsNode = parserServices.esTreeNodeToTSNodeMap.get(node);
135
+ const expectedType = checker.getContextualType(tsNode);
136
+ if (expectedType != null && isVoidReturningFunctionType(expectedType)) {
137
+ reportIfNonVoidFunction(node);
138
+ return true;
139
+ }
140
+ return false;
141
+ }
142
+ /**
143
+ * Finds errors in function calls.
144
+ *
145
+ * When checking arguments, we also manually figure out the argument types
146
+ * by iterating over all the function signatures.
147
+ * Thanks to this, we can find arguments like `(() => void) | (() => any)`
148
+ * and treat them as void too.
149
+ * This is done to also support checking functions like `addEventListener`
150
+ * which have overloads where one callback returns any.
151
+ *
152
+ * Implementation mostly based on no-misused-promises,
153
+ * which does this to find `(() => void) | (() => NotThenable)`
154
+ * and report them too.
155
+ */
156
+ function checkFunctionCallNode(callNode) {
157
+ const callTsNode = parserServices.esTreeNodeToTSNodeMap.get(callNode);
158
+ const funcType = checker.getTypeAtLocation(callTsNode.expression);
159
+ const funcSignatures = tsutils
160
+ .unionConstituents(funcType)
161
+ .flatMap(type => ts.isCallExpression(callTsNode)
162
+ ? type.getCallSignatures()
163
+ : type.getConstructSignatures());
164
+ for (const [argIdx, argNode] of callNode.arguments.entries()) {
165
+ if (argNode.type === utils_1.AST_NODE_TYPES.SpreadElement) {
166
+ continue;
167
+ }
168
+ // Check against the contextual type first
169
+ if (checkExpressionNode(argNode)) {
170
+ continue;
171
+ }
172
+ // Check against the types from all of the call signatures
173
+ const argExpectedReturnTypes = funcSignatures
174
+ .map(s => s.parameters[argIdx])
175
+ .filter(Boolean)
176
+ .map(param => checker.getTypeOfSymbolAtLocation(param, callTsNode.expression))
177
+ .flatMap(paramType => tsutils.unionConstituents(paramType))
178
+ .flatMap(paramType => paramType.getCallSignatures())
179
+ .map(paramSignature => paramSignature.getReturnType());
180
+ if (
181
+ // At least one return type is void
182
+ argExpectedReturnTypes.some(type => tsutils.isTypeFlagSet(type, ts.TypeFlags.Void)) &&
183
+ // The rest are nullish or any
184
+ argExpectedReturnTypes.every(type => tsutils.isTypeFlagSet(type, ts.TypeFlags.VoidLike |
185
+ ts.TypeFlags.Undefined |
186
+ ts.TypeFlags.Null |
187
+ ts.TypeFlags.Any |
188
+ ts.TypeFlags.Never))) {
189
+ // We treat this argument as void even though it might be technically any.
190
+ reportIfNonVoidFunction(argNode);
191
+ }
192
+ }
193
+ }
194
+ /**
195
+ * Finds errors in an object property.
196
+ *
197
+ * Object properties require different logic
198
+ * when the property is a method shorthand.
199
+ */
200
+ function checkObjectPropertyNode(propNode) {
201
+ const valueNode = propNode.value;
202
+ const propTsNode = parserServices.esTreeNodeToTSNodeMap.get(propNode);
203
+ if (propTsNode.kind === ts.SyntaxKind.MethodDeclaration) {
204
+ // Object property is a method shorthand.
205
+ if (propTsNode.name.kind === ts.SyntaxKind.ComputedPropertyName) {
206
+ // Don't check object methods with computed name.
207
+ return;
208
+ }
209
+ const objTsNode = propTsNode.parent;
210
+ const objType = checker.getContextualType(objTsNode);
211
+ if (objType == null) {
212
+ // Expected object type is unknown.
213
+ return;
214
+ }
215
+ const propSymbol = checker.getPropertyOfType(objType, propTsNode.name.text);
216
+ if (propSymbol == null) {
217
+ // Expected object type is known, but it doesn't have this method.
218
+ return;
219
+ }
220
+ const propExpectedType = checker.getTypeOfSymbolAtLocation(propSymbol, propTsNode);
221
+ if (isVoidReturningFunctionType(propExpectedType)) {
222
+ reportIfNonVoidFunction(valueNode);
223
+ }
224
+ return;
225
+ }
226
+ // Object property is a regular property.
227
+ checkExpressionNode(valueNode);
228
+ }
229
+ /**
230
+ * Finds errors in a class property.
231
+ *
232
+ * In addition to the regular check against the contextual type,
233
+ * we also check against the base class property (when the class extends another class)
234
+ * and the implemented interfaces (when the class implements an interface).
235
+ */
236
+ function checkClassPropertyNode(propNode) {
237
+ if (propNode.value == null) {
238
+ return;
239
+ }
240
+ // Check in comparison to the base types.
241
+ for (const { baseMemberType } of util.getBaseTypesOfClassMember(parserServices, propNode)) {
242
+ if (isVoidReturningFunctionType(baseMemberType)) {
243
+ reportIfNonVoidFunction(propNode.value);
244
+ return; // Report at most one error.
245
+ }
246
+ }
247
+ // Check in comparison to the contextual type.
248
+ checkExpressionNode(propNode.value);
249
+ }
250
+ /**
251
+ * Finds errors in a class method.
252
+ *
253
+ * We check against the base class method (when the class extends another class)
254
+ * and the implemented interfaces (when the class implements an interface).
255
+ */
256
+ function checkClassMethodNode(methodNode) {
257
+ if (methodNode.value.type === utils_1.AST_NODE_TYPES.TSEmptyBodyFunctionExpression) {
258
+ return;
259
+ }
260
+ // Check in comparison to the base types.
261
+ for (const { baseMemberType } of util.getBaseTypesOfClassMember(parserServices, methodNode)) {
262
+ if (isVoidReturningFunctionType(baseMemberType)) {
263
+ reportIfNonVoidFunction(methodNode.value);
264
+ return; // Report at most one error.
265
+ }
266
+ }
267
+ }
268
+ /**
269
+ * Reports an error if the provided node is not allowed in a void function context.
270
+ */
271
+ function reportIfNonVoidFunction(funcNode) {
272
+ const allowedReturnType = ts.TypeFlags.Void |
273
+ ts.TypeFlags.Never |
274
+ ts.TypeFlags.Undefined |
275
+ (options.allowReturnAny ? ts.TypeFlags.Any : 0);
276
+ const tsNode = parserServices.esTreeNodeToTSNodeMap.get(funcNode);
277
+ const actualType = checker.getApparentType(checker.getTypeAtLocation(tsNode));
278
+ if (tsutils
279
+ .getCallSignaturesOfType(actualType)
280
+ .map(signature => signature.getReturnType())
281
+ .flatMap(returnType => tsutils.unionConstituents(returnType))
282
+ .every(type => tsutils.isTypeFlagSet(type, allowedReturnType))) {
283
+ // The function is already void.
284
+ return;
285
+ }
286
+ if (funcNode.type !== utils_1.AST_NODE_TYPES.ArrowFunctionExpression &&
287
+ funcNode.type !== utils_1.AST_NODE_TYPES.FunctionExpression) {
288
+ // The provided function is not a function literal.
289
+ // Report a generic error.
290
+ return context.report({
291
+ node: funcNode,
292
+ messageId: `nonVoidFunc`,
293
+ });
294
+ }
295
+ // The provided function is a function literal.
296
+ if (funcNode.generator) {
297
+ // The provided function is a generator function.
298
+ // Generator functions are not allowed.
299
+ return context.report({
300
+ loc: util.getFunctionHeadLoc(funcNode, sourceCode),
301
+ messageId: `nonVoidFunc`,
302
+ });
303
+ }
304
+ if (funcNode.async) {
305
+ // The provided function is an async function.
306
+ // Async functions aren't allowed.
307
+ return context.report({
308
+ loc: util.getFunctionHeadLoc(funcNode, sourceCode),
309
+ messageId: `asyncFunc`,
310
+ });
311
+ }
312
+ if (funcNode.body.type !== utils_1.AST_NODE_TYPES.BlockStatement) {
313
+ // The provided function is an arrow function shorthand without braces.
314
+ return context.report({
315
+ node: funcNode.body,
316
+ messageId: `nonVoidReturn`,
317
+ });
318
+ }
319
+ // The function is a regular or arrow function with a block body.
320
+ // Check return type annotation.
321
+ if (funcNode.returnType != null) {
322
+ // The provided function has an explicit return type annotation.
323
+ const typeAnnotationNode = funcNode.returnType.typeAnnotation;
324
+ if (typeAnnotationNode.type !== utils_1.AST_NODE_TYPES.TSVoidKeyword) {
325
+ // The explicit return type is not `void`.
326
+ return context.report({
327
+ node: typeAnnotationNode,
328
+ messageId: `nonVoidFunc`,
329
+ });
330
+ }
331
+ }
332
+ // Iterate over all function's return statements.
333
+ for (const statement of util.walkStatements(funcNode.body.body)) {
334
+ if (statement.type !== utils_1.AST_NODE_TYPES.ReturnStatement ||
335
+ statement.argument == null) {
336
+ // We only care about return statements with a value.
337
+ continue;
338
+ }
339
+ const returnType = checker.getTypeAtLocation(parserServices.esTreeNodeToTSNodeMap.get(statement.argument));
340
+ if (tsutils.isTypeFlagSet(returnType, allowedReturnType)) {
341
+ // Only visit return statements with invalid type.
342
+ continue;
343
+ }
344
+ // This return statement causes the non-void return type.
345
+ const returnKeyword = util.nullThrows(sourceCode.getFirstToken(statement, {
346
+ filter: token => token.value === 'return',
347
+ }), util.NullThrowsReasons.MissingToken('return keyword', statement.type));
348
+ context.report({
349
+ node: returnKeyword,
350
+ messageId: `nonVoidReturn`,
351
+ });
352
+ }
353
+ // No invalid returns found. The function is allowed.
354
+ }
355
+ },
356
+ });
@@ -0,0 +1,11 @@
1
+ import type { TSESTree, ParserServicesWithTypeInformation } from '@typescript-eslint/utils';
2
+ import type * as ts from 'typescript';
3
+ /**
4
+ * Given a member of a class which extends another class or implements an interface,
5
+ * yields the corresponding member type for each of the base class/interfaces.
6
+ */
7
+ export declare function getBaseTypesOfClassMember(services: ParserServicesWithTypeInformation, memberNode: TSESTree.MethodDefinition | TSESTree.PropertyDefinition): Generator<{
8
+ baseType: ts.Type;
9
+ baseMemberType: ts.Type;
10
+ heritageToken: ts.SyntaxKind.ExtendsKeyword | ts.SyntaxKind.ImplementsKeyword;
11
+ }>;
@@ -0,0 +1,31 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.getBaseTypesOfClassMember = getBaseTypesOfClassMember;
4
+ /**
5
+ * Given a member of a class which extends another class or implements an interface,
6
+ * yields the corresponding member type for each of the base class/interfaces.
7
+ */
8
+ function* getBaseTypesOfClassMember(services, memberNode) {
9
+ const memberTsNode = services.esTreeNodeToTSNodeMap.get(memberNode);
10
+ if (memberTsNode.name == null) {
11
+ return;
12
+ }
13
+ const checker = services.program.getTypeChecker();
14
+ const memberSymbol = checker.getSymbolAtLocation(memberTsNode.name);
15
+ if (memberSymbol == null) {
16
+ return;
17
+ }
18
+ const classNode = memberTsNode.parent;
19
+ for (const clauseNode of classNode.heritageClauses ?? []) {
20
+ for (const baseTypeNode of clauseNode.types) {
21
+ const baseType = checker.getTypeAtLocation(baseTypeNode);
22
+ const baseMemberSymbol = checker.getPropertyOfType(baseType, memberSymbol.name);
23
+ if (baseMemberSymbol == null) {
24
+ continue;
25
+ }
26
+ const baseMemberType = checker.getTypeOfSymbolAtLocation(baseMemberSymbol, memberTsNode);
27
+ const heritageToken = clauseNode.token;
28
+ yield { baseMemberType, baseType, heritageToken };
29
+ }
30
+ }
31
+ }
@@ -3,6 +3,7 @@ export * from './astUtils';
3
3
  export * from './baseTypeUtils';
4
4
  export * from './collectUnusedVariables';
5
5
  export * from './createRule';
6
+ export * from './getBaseTypesOfClassMember';
6
7
  export * from './getFixOrSuggest';
7
8
  export * from './getFunctionHeadLoc';
8
9
  export * from './getOperatorPrecedence';
@@ -29,6 +30,7 @@ export * from './getValueOfLiteralType';
29
30
  export * from './isHigherPrecedenceThanAwait';
30
31
  export * from './skipChainExpression';
31
32
  export * from './truthinessUtils';
33
+ export * from './walkStatements';
32
34
  export * from '@typescript-eslint/type-utils';
33
35
  export declare const applyDefault: typeof ESLintUtils.applyDefault, deepMerge: typeof ESLintUtils.deepMerge, getParserServices: typeof ESLintUtils.getParserServices, isObjectNotArray: typeof ESLintUtils.isObjectNotArray, nullThrows: typeof ESLintUtils.nullThrows, NullThrowsReasons: {
34
36
  readonly MissingParent: "Expected node to have a parent.";
@@ -20,6 +20,7 @@ __exportStar(require("./astUtils"), exports);
20
20
  __exportStar(require("./baseTypeUtils"), exports);
21
21
  __exportStar(require("./collectUnusedVariables"), exports);
22
22
  __exportStar(require("./createRule"), exports);
23
+ __exportStar(require("./getBaseTypesOfClassMember"), exports);
23
24
  __exportStar(require("./getFixOrSuggest"), exports);
24
25
  __exportStar(require("./getFunctionHeadLoc"), exports);
25
26
  __exportStar(require("./getOperatorPrecedence"), exports);
@@ -46,6 +47,7 @@ __exportStar(require("./getValueOfLiteralType"), exports);
46
47
  __exportStar(require("./isHigherPrecedenceThanAwait"), exports);
47
48
  __exportStar(require("./skipChainExpression"), exports);
48
49
  __exportStar(require("./truthinessUtils"), exports);
50
+ __exportStar(require("./walkStatements"), exports);
49
51
  // this is done for convenience - saves migrating all of the old rules
50
52
  __exportStar(require("@typescript-eslint/type-utils"), exports);
51
53
  exports.applyDefault = utils_1.ESLintUtils.applyDefault, exports.deepMerge = utils_1.ESLintUtils.deepMerge, exports.getParserServices = utils_1.ESLintUtils.getParserServices, exports.isObjectNotArray = utils_1.ESLintUtils.isObjectNotArray, exports.nullThrows = utils_1.ESLintUtils.nullThrows, exports.NullThrowsReasons = utils_1.ESLintUtils.NullThrowsReasons;
@@ -0,0 +1,7 @@
1
+ import type { TSESTree } from '@typescript-eslint/utils';
2
+ /**
3
+ * Yields all statement nodes in a block, including nested blocks.
4
+ *
5
+ * You can use it to find all return statements in a function body.
6
+ */
7
+ export declare function walkStatements(body: readonly TSESTree.Statement[]): Generator<TSESTree.Statement>;
@@ -0,0 +1,56 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.walkStatements = walkStatements;
4
+ const utils_1 = require("@typescript-eslint/utils");
5
+ /**
6
+ * Yields all statement nodes in a block, including nested blocks.
7
+ *
8
+ * You can use it to find all return statements in a function body.
9
+ */
10
+ function* walkStatements(body) {
11
+ for (const statement of body) {
12
+ switch (statement.type) {
13
+ case utils_1.AST_NODE_TYPES.BlockStatement: {
14
+ yield* walkStatements(statement.body);
15
+ continue;
16
+ }
17
+ case utils_1.AST_NODE_TYPES.SwitchStatement: {
18
+ for (const switchCase of statement.cases) {
19
+ yield* walkStatements(switchCase.consequent);
20
+ }
21
+ continue;
22
+ }
23
+ case utils_1.AST_NODE_TYPES.IfStatement: {
24
+ yield* walkStatements([statement.consequent]);
25
+ if (statement.alternate) {
26
+ yield* walkStatements([statement.alternate]);
27
+ }
28
+ continue;
29
+ }
30
+ case utils_1.AST_NODE_TYPES.WhileStatement:
31
+ case utils_1.AST_NODE_TYPES.DoWhileStatement:
32
+ case utils_1.AST_NODE_TYPES.ForStatement:
33
+ case utils_1.AST_NODE_TYPES.ForInStatement:
34
+ case utils_1.AST_NODE_TYPES.ForOfStatement:
35
+ case utils_1.AST_NODE_TYPES.WithStatement:
36
+ case utils_1.AST_NODE_TYPES.LabeledStatement: {
37
+ yield* walkStatements([statement.body]);
38
+ continue;
39
+ }
40
+ case utils_1.AST_NODE_TYPES.TryStatement: {
41
+ yield* walkStatements([statement.block]);
42
+ if (statement.handler) {
43
+ yield* walkStatements([statement.handler.body]);
44
+ }
45
+ if (statement.finalizer) {
46
+ yield* walkStatements([statement.finalizer]);
47
+ }
48
+ continue;
49
+ }
50
+ default: {
51
+ yield statement;
52
+ continue;
53
+ }
54
+ }
55
+ }
56
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@typescript-eslint/eslint-plugin",
3
- "version": "8.52.1-alpha.12",
3
+ "version": "8.52.1-alpha.13",
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.12.2",
62
- "@typescript-eslint/scope-manager": "8.52.1-alpha.12",
63
- "@typescript-eslint/type-utils": "8.52.1-alpha.12",
64
- "@typescript-eslint/utils": "8.52.1-alpha.12",
65
- "@typescript-eslint/visitor-keys": "8.52.1-alpha.12",
62
+ "@typescript-eslint/scope-manager": "8.52.1-alpha.13",
63
+ "@typescript-eslint/type-utils": "8.52.1-alpha.13",
64
+ "@typescript-eslint/utils": "8.52.1-alpha.13",
65
+ "@typescript-eslint/visitor-keys": "8.52.1-alpha.13",
66
66
  "ignore": "^7.0.5",
67
67
  "natural-compare": "^1.4.0",
68
68
  "ts-api-utils": "^2.4.0"
@@ -70,8 +70,8 @@
70
70
  "devDependencies": {
71
71
  "@types/mdast": "^4.0.4",
72
72
  "@types/natural-compare": "*",
73
- "@typescript-eslint/rule-schema-to-typescript-types": "8.52.1-alpha.12",
74
- "@typescript-eslint/rule-tester": "8.52.1-alpha.12",
73
+ "@typescript-eslint/rule-schema-to-typescript-types": "8.52.1-alpha.13",
74
+ "@typescript-eslint/rule-tester": "8.52.1-alpha.13",
75
75
  "@vitest/coverage-v8": "^3.2.4",
76
76
  "ajv": "^6.12.6",
77
77
  "eslint": "*",
@@ -90,7 +90,7 @@
90
90
  "vitest": "^3.2.4"
91
91
  },
92
92
  "peerDependencies": {
93
- "@typescript-eslint/parser": "^8.52.1-alpha.12",
93
+ "@typescript-eslint/parser": "^8.52.1-alpha.13",
94
94
  "eslint": "^8.57.0 || ^9.0.0",
95
95
  "typescript": ">=4.8.4 <6.0.0"
96
96
  },