Thanks to visit codestin.com
Credit goes to github.com

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 <[email protected]>
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)