Skip to content

Commit f991764

Browse files
bradzacherarmano2
andauthored
chore(eslint-plugin): refactor explicit return type rules to share code (typescript-eslint#1493)
Co-authored-by: Armano <armano2@users.noreply.github.com>
1 parent c8dfac3 commit f991764

File tree

6 files changed

+452
-645
lines changed

6 files changed

+452
-645
lines changed

.vscode/settings.json

Lines changed: 2 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -4,14 +4,8 @@
44
"eslint.validate": [
55
"javascript",
66
"javascriptreact",
7-
{
8-
"language": "typescript",
9-
"autoFix": true
10-
},
11-
{
12-
"language": "typescriptreact",
13-
"autoFix": true
14-
}
7+
"typescript",
8+
"typescriptreact"
159
],
1610

1711
// When enabled, will trim trailing whitespace when saving a file.
Lines changed: 25 additions & 301 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,9 @@
1-
import {
2-
TSESTree,
3-
AST_NODE_TYPES,
4-
AST_TOKEN_TYPES,
5-
} from '@typescript-eslint/experimental-utils';
1+
import { TSESTree } from '@typescript-eslint/experimental-utils';
62
import * as util from '../util';
3+
import {
4+
checkFunctionReturnType,
5+
checkFunctionExpressionReturnType,
6+
} from '../util/explicitReturnTypeUtils';
77

88
type Options = [
99
{
@@ -60,303 +60,27 @@ export default util.createRule<Options, MessageIds>({
6060
create(context, [options]) {
6161
const sourceCode = context.getSourceCode();
6262

63-
/**
64-
* Returns start column position
65-
* @param node
66-
*/
67-
function getLocStart(
68-
node:
69-
| TSESTree.ArrowFunctionExpression
70-
| TSESTree.FunctionDeclaration
71-
| TSESTree.FunctionExpression,
72-
): TSESTree.LineAndColumnData {
73-
/* highlight method name */
74-
const parent = node.parent;
75-
if (
76-
parent &&
77-
(parent.type === AST_NODE_TYPES.MethodDefinition ||
78-
(parent.type === AST_NODE_TYPES.Property && parent.method))
79-
) {
80-
return parent.loc.start;
81-
}
82-
83-
return node.loc.start;
84-
}
85-
86-
/**
87-
* Returns end column position
88-
* @param node
89-
*/
90-
function getLocEnd(
91-
node:
92-
| TSESTree.ArrowFunctionExpression
93-
| TSESTree.FunctionDeclaration
94-
| TSESTree.FunctionExpression,
95-
): TSESTree.LineAndColumnData {
96-
/* highlight `=>` */
97-
if (node.type === AST_NODE_TYPES.ArrowFunctionExpression) {
98-
return sourceCode.getTokenBefore(
99-
node.body,
100-
token =>
101-
token.type === AST_TOKEN_TYPES.Punctuator && token.value === '=>',
102-
)!.loc.end;
103-
}
104-
105-
return sourceCode.getTokenBefore(node.body!)!.loc.end;
106-
}
107-
108-
/**
109-
* Checks if a node is a constructor.
110-
* @param node The node to check
111-
*/
112-
function isConstructor(node: TSESTree.Node | undefined): boolean {
113-
return (
114-
!!node &&
115-
node.type === AST_NODE_TYPES.MethodDefinition &&
116-
node.kind === 'constructor'
117-
);
118-
}
119-
120-
/**
121-
* Checks if a node is a setter.
122-
*/
123-
function isSetter(node: TSESTree.Node | undefined): boolean {
124-
return (
125-
!!node &&
126-
(node.type === AST_NODE_TYPES.MethodDefinition ||
127-
node.type === AST_NODE_TYPES.Property) &&
128-
node.kind === 'set'
129-
);
130-
}
131-
132-
/**
133-
* Checks if a node is a variable declarator with a type annotation.
134-
* `const x: Foo = ...`
135-
*/
136-
function isVariableDeclaratorWithTypeAnnotation(
137-
node: TSESTree.Node,
138-
): boolean {
139-
return (
140-
node.type === AST_NODE_TYPES.VariableDeclarator &&
141-
!!node.id.typeAnnotation
142-
);
143-
}
144-
145-
/**
146-
* Checks if a node is a class property with a type annotation.
147-
* `public x: Foo = ...`
148-
*/
149-
function isClassPropertyWithTypeAnnotation(node: TSESTree.Node): boolean {
150-
return (
151-
node.type === AST_NODE_TYPES.ClassProperty && !!node.typeAnnotation
152-
);
153-
}
154-
155-
/**
156-
* Checks if a node belongs to:
157-
* new Foo(() => {})
158-
* ^^^^^^^^
159-
*/
160-
function isConstructorArgument(parent: TSESTree.Node): boolean {
161-
return parent.type === AST_NODE_TYPES.NewExpression;
162-
}
163-
164-
/**
165-
* Checks if a node belongs to:
166-
* `const x: Foo = { prop: () => {} }`
167-
* `const x = { prop: () => {} } as Foo`
168-
* `const x = <Foo>{ prop: () => {} }`
169-
*/
170-
function isPropertyOfObjectWithType(
171-
property: TSESTree.Node | undefined,
172-
): boolean {
173-
if (!property || property.type !== AST_NODE_TYPES.Property) {
174-
return false;
175-
}
176-
const objectExpr = property.parent; // this shouldn't happen, checking just in case
177-
/* istanbul ignore if */ if (
178-
!objectExpr ||
179-
objectExpr.type !== AST_NODE_TYPES.ObjectExpression
180-
) {
181-
return false;
182-
}
183-
184-
const parent = objectExpr.parent; // this shouldn't happen, checking just in case
185-
/* istanbul ignore if */ if (!parent) {
186-
return false;
187-
}
188-
189-
return (
190-
util.isTypeAssertion(parent) ||
191-
isClassPropertyWithTypeAnnotation(parent) ||
192-
isVariableDeclaratorWithTypeAnnotation(parent) ||
193-
isFunctionArgument(parent)
194-
);
195-
}
196-
197-
/**
198-
* Checks if a function belongs to:
199-
* `() => () => ...`
200-
* `() => function () { ... }`
201-
* `() => { return () => ... }`
202-
* `() => { return function () { ... } }`
203-
* `function fn() { return () => ... }`
204-
* `function fn() { return function() { ... } }`
205-
*/
206-
function doesImmediatelyReturnFunctionExpression({
207-
body,
208-
}:
209-
| TSESTree.ArrowFunctionExpression
210-
| TSESTree.FunctionDeclaration
211-
| TSESTree.FunctionExpression): boolean {
212-
// Should always have a body; really checking just in case
213-
/* istanbul ignore if */ if (!body) {
214-
return false;
215-
}
216-
217-
// Check if body is a block with a single statement
218-
if (
219-
body.type === AST_NODE_TYPES.BlockStatement &&
220-
body.body.length === 1
221-
) {
222-
const [statement] = body.body;
223-
224-
// Check if that statement is a return statement with an argument
225-
if (
226-
statement.type === AST_NODE_TYPES.ReturnStatement &&
227-
!!statement.argument
228-
) {
229-
// If so, check that returned argument as body
230-
body = statement.argument;
231-
}
232-
}
233-
234-
// Check if the body being returned is a function expression
235-
return (
236-
body.type === AST_NODE_TYPES.ArrowFunctionExpression ||
237-
body.type === AST_NODE_TYPES.FunctionExpression
238-
);
239-
}
240-
241-
/**
242-
* Checks if a node belongs to:
243-
* `foo(() => 1)`
244-
*/
245-
function isFunctionArgument(
246-
parent: TSESTree.Node,
247-
callee?: TSESTree.ArrowFunctionExpression | TSESTree.FunctionExpression,
248-
): boolean {
249-
return (
250-
(parent.type === AST_NODE_TYPES.CallExpression ||
251-
parent.type === AST_NODE_TYPES.OptionalCallExpression) &&
252-
// make sure this isn't an IIFE
253-
parent.callee !== callee
254-
);
255-
}
256-
257-
/**
258-
* Checks if a function belongs to:
259-
* `() => ({ action: 'xxx' }) as const`
260-
*/
261-
function returnsConstAssertionDirectly(
262-
node: TSESTree.ArrowFunctionExpression,
263-
): boolean {
264-
const { body } = node;
265-
if (util.isTypeAssertion(body)) {
266-
const { typeAnnotation } = body;
267-
if (typeAnnotation.type === AST_NODE_TYPES.TSTypeReference) {
268-
const { typeName } = typeAnnotation;
269-
if (
270-
typeName.type === AST_NODE_TYPES.Identifier &&
271-
typeName.name === 'const'
272-
) {
273-
return true;
274-
}
275-
}
276-
}
277-
278-
return false;
279-
}
280-
281-
/**
282-
* Checks if a function declaration/expression has a return type.
283-
*/
284-
function checkFunctionReturnType(
285-
node:
286-
| TSESTree.ArrowFunctionExpression
287-
| TSESTree.FunctionDeclaration
288-
| TSESTree.FunctionExpression,
289-
): void {
290-
if (
291-
options.allowHigherOrderFunctions &&
292-
doesImmediatelyReturnFunctionExpression(node)
293-
) {
294-
return;
295-
}
296-
297-
if (
298-
node.returnType ||
299-
isConstructor(node.parent) ||
300-
isSetter(node.parent)
301-
) {
302-
return;
303-
}
304-
305-
context.report({
306-
node,
307-
loc: { start: getLocStart(node), end: getLocEnd(node) },
308-
messageId: 'missingReturnType',
309-
});
310-
}
311-
312-
/**
313-
* Checks if a function declaration/expression has a return type.
314-
*/
315-
function checkFunctionExpressionReturnType(
316-
node: TSESTree.ArrowFunctionExpression | TSESTree.FunctionExpression,
317-
): void {
318-
// Should always have a parent; checking just in case
319-
/* istanbul ignore else */ if (node.parent) {
320-
if (options.allowTypedFunctionExpressions) {
321-
if (
322-
util.isTypeAssertion(node.parent) ||
323-
isVariableDeclaratorWithTypeAnnotation(node.parent) ||
324-
isClassPropertyWithTypeAnnotation(node.parent) ||
325-
isPropertyOfObjectWithType(node.parent) ||
326-
isFunctionArgument(node.parent, node) ||
327-
isConstructorArgument(node.parent)
328-
) {
329-
return;
330-
}
331-
}
332-
333-
if (
334-
options.allowExpressions &&
335-
node.parent.type !== AST_NODE_TYPES.VariableDeclarator &&
336-
node.parent.type !== AST_NODE_TYPES.MethodDefinition &&
337-
node.parent.type !== AST_NODE_TYPES.ExportDefaultDeclaration &&
338-
node.parent.type !== AST_NODE_TYPES.ClassProperty
339-
) {
340-
return;
341-
}
342-
}
343-
344-
// https://github.com/typescript-eslint/typescript-eslint/issues/653
345-
if (
346-
node.type === AST_NODE_TYPES.ArrowFunctionExpression &&
347-
options.allowDirectConstAssertionInArrowFunctions &&
348-
returnsConstAssertionDirectly(node)
349-
) {
350-
return;
351-
}
352-
353-
checkFunctionReturnType(node);
354-
}
355-
35663
return {
357-
ArrowFunctionExpression: checkFunctionExpressionReturnType,
358-
FunctionDeclaration: checkFunctionReturnType,
359-
FunctionExpression: checkFunctionExpressionReturnType,
64+
'ArrowFunctionExpression, FunctionExpression'(
65+
node: TSESTree.ArrowFunctionExpression | TSESTree.FunctionExpression,
66+
): void {
67+
checkFunctionExpressionReturnType(node, options, sourceCode, loc =>
68+
context.report({
69+
node,
70+
loc,
71+
messageId: 'missingReturnType',
72+
}),
73+
);
74+
},
75+
FunctionDeclaration(node): void {
76+
checkFunctionReturnType(node, options, sourceCode, loc =>
77+
context.report({
78+
node,
79+
loc,
80+
messageId: 'missingReturnType',
81+
}),
82+
);
83+
},
36084
};
36185
},
36286
});

0 commit comments

Comments
 (0)
pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy