UNPKG

8.92 kBJavaScriptView Raw
1"use strict";
2Object.defineProperty(exports, "__esModule", { value: true });
3exports.ancestorHasReturnType = exports.isValidFunctionExpressionReturnType = exports.isTypedFunctionExpression = exports.doesImmediatelyReturnFunctionExpression = exports.checkFunctionReturnType = exports.checkFunctionExpressionReturnType = void 0;
4const utils_1 = require("@typescript-eslint/utils");
5const astUtils_1 = require("./astUtils");
6const getFunctionHeadLoc_1 = require("./getFunctionHeadLoc");
7/**
8 * Checks if a node is a variable declarator with a type annotation.
9 * ```
10 * const x: Foo = ...
11 * ```
12 */
13function isVariableDeclaratorWithTypeAnnotation(node) {
14 return (node.type === utils_1.AST_NODE_TYPES.VariableDeclarator && !!node.id.typeAnnotation);
15}
16/**
17 * Checks if a node is a class property with a type annotation.
18 * ```
19 * public x: Foo = ...
20 * ```
21 */
22function isPropertyDefinitionWithTypeAnnotation(node) {
23 return (node.type === utils_1.AST_NODE_TYPES.PropertyDefinition && !!node.typeAnnotation);
24}
25/**
26 * Checks if a node belongs to:
27 * ```
28 * new Foo(() => {})
29 * ^^^^^^^^
30 * ```
31 */
32function isConstructorArgument(node) {
33 return node.type === utils_1.AST_NODE_TYPES.NewExpression;
34}
35/**
36 * Checks if a node is a property or a nested property of a typed object:
37 * ```
38 * const x: Foo = { prop: () => {} }
39 * const x = { prop: () => {} } as Foo
40 * const x = <Foo>{ prop: () => {} }
41 * const x: Foo = { bar: { prop: () => {} } }
42 * ```
43 */
44function isPropertyOfObjectWithType(property) {
45 if (!property || property.type !== utils_1.AST_NODE_TYPES.Property) {
46 return false;
47 }
48 const objectExpr = property.parent; // this shouldn't happen, checking just in case
49 /* istanbul ignore if */ if (!objectExpr ||
50 objectExpr.type !== utils_1.AST_NODE_TYPES.ObjectExpression) {
51 return false;
52 }
53 const parent = objectExpr.parent; // this shouldn't happen, checking just in case
54 /* istanbul ignore if */ if (!parent) {
55 return false;
56 }
57 return ((0, astUtils_1.isTypeAssertion)(parent) ||
58 isPropertyDefinitionWithTypeAnnotation(parent) ||
59 isVariableDeclaratorWithTypeAnnotation(parent) ||
60 isFunctionArgument(parent) ||
61 isPropertyOfObjectWithType(parent));
62}
63/**
64 * Checks if a function belongs to:
65 * ```
66 * () => () => ...
67 * () => function () { ... }
68 * () => { return () => ... }
69 * () => { return function () { ... } }
70 * function fn() { return () => ... }
71 * function fn() { return function() { ... } }
72 * ```
73 */
74function doesImmediatelyReturnFunctionExpression({ body, }) {
75 // Should always have a body; really checking just in case
76 /* istanbul ignore if */ if (!body) {
77 return false;
78 }
79 // Check if body is a block with a single statement
80 if (body.type === utils_1.AST_NODE_TYPES.BlockStatement && body.body.length === 1) {
81 const [statement] = body.body;
82 // Check if that statement is a return statement with an argument
83 if (statement.type === utils_1.AST_NODE_TYPES.ReturnStatement &&
84 !!statement.argument) {
85 // If so, check that returned argument as body
86 body = statement.argument;
87 }
88 }
89 // Check if the body being returned is a function expression
90 return (body.type === utils_1.AST_NODE_TYPES.ArrowFunctionExpression ||
91 body.type === utils_1.AST_NODE_TYPES.FunctionExpression);
92}
93exports.doesImmediatelyReturnFunctionExpression = doesImmediatelyReturnFunctionExpression;
94/**
95 * Checks if a node belongs to:
96 * ```
97 * foo(() => 1)
98 * ```
99 */
100function isFunctionArgument(parent, callee) {
101 return (parent.type === utils_1.AST_NODE_TYPES.CallExpression &&
102 // make sure this isn't an IIFE
103 parent.callee !== callee);
104}
105/**
106 * Checks if a function belongs to:
107 * ```
108 * () => ({ action: 'xxx' } as const)
109 * ```
110 */
111function returnsConstAssertionDirectly(node) {
112 const { body } = node;
113 if ((0, astUtils_1.isTypeAssertion)(body)) {
114 const { typeAnnotation } = body;
115 if (typeAnnotation.type === utils_1.AST_NODE_TYPES.TSTypeReference) {
116 const { typeName } = typeAnnotation;
117 if (typeName.type === utils_1.AST_NODE_TYPES.Identifier &&
118 typeName.name === 'const') {
119 return true;
120 }
121 }
122 }
123 return false;
124}
125/**
126 * True when the provided function expression is typed.
127 */
128function isTypedFunctionExpression(node, options) {
129 const parent = utils_1.ESLintUtils.nullThrows(node.parent, utils_1.ESLintUtils.NullThrowsReasons.MissingParent);
130 if (!options.allowTypedFunctionExpressions) {
131 return false;
132 }
133 return ((0, astUtils_1.isTypeAssertion)(parent) ||
134 isVariableDeclaratorWithTypeAnnotation(parent) ||
135 isPropertyDefinitionWithTypeAnnotation(parent) ||
136 isPropertyOfObjectWithType(parent) ||
137 isFunctionArgument(parent, node) ||
138 isConstructorArgument(parent));
139}
140exports.isTypedFunctionExpression = isTypedFunctionExpression;
141/**
142 * Check whether the function expression return type is either typed or valid
143 * with the provided options.
144 */
145function isValidFunctionExpressionReturnType(node, options) {
146 if (isTypedFunctionExpression(node, options)) {
147 return true;
148 }
149 const parent = utils_1.ESLintUtils.nullThrows(node.parent, utils_1.ESLintUtils.NullThrowsReasons.MissingParent);
150 if (options.allowExpressions &&
151 parent.type !== utils_1.AST_NODE_TYPES.VariableDeclarator &&
152 parent.type !== utils_1.AST_NODE_TYPES.MethodDefinition &&
153 parent.type !== utils_1.AST_NODE_TYPES.ExportDefaultDeclaration &&
154 parent.type !== utils_1.AST_NODE_TYPES.PropertyDefinition) {
155 return true;
156 }
157 // https://github.com/typescript-eslint/typescript-eslint/issues/653
158 return (options.allowDirectConstAssertionInArrowFunctions === true &&
159 node.type === utils_1.AST_NODE_TYPES.ArrowFunctionExpression &&
160 returnsConstAssertionDirectly(node));
161}
162exports.isValidFunctionExpressionReturnType = isValidFunctionExpressionReturnType;
163/**
164 * Check that the function expression or declaration is valid.
165 */
166function isValidFunctionReturnType(node, options) {
167 if (options.allowHigherOrderFunctions &&
168 doesImmediatelyReturnFunctionExpression(node)) {
169 return true;
170 }
171 return (node.returnType != null ||
172 (0, astUtils_1.isConstructor)(node.parent) ||
173 (0, astUtils_1.isSetter)(node.parent));
174}
175/**
176 * Checks if a function declaration/expression has a return type.
177 */
178function checkFunctionReturnType(node, options, sourceCode, report) {
179 if (isValidFunctionReturnType(node, options)) {
180 return;
181 }
182 report((0, getFunctionHeadLoc_1.getFunctionHeadLoc)(node, sourceCode));
183}
184exports.checkFunctionReturnType = checkFunctionReturnType;
185/**
186 * Checks if a function declaration/expression has a return type.
187 */
188function checkFunctionExpressionReturnType(node, options, sourceCode, report) {
189 if (isValidFunctionExpressionReturnType(node, options)) {
190 return;
191 }
192 checkFunctionReturnType(node, options, sourceCode, report);
193}
194exports.checkFunctionExpressionReturnType = checkFunctionExpressionReturnType;
195/**
196 * Check whether any ancestor of the provided function has a valid return type.
197 */
198function ancestorHasReturnType(node) {
199 let ancestor = node.parent;
200 if ((ancestor === null || ancestor === void 0 ? void 0 : ancestor.type) === utils_1.AST_NODE_TYPES.Property) {
201 ancestor = ancestor.value;
202 }
203 // if the ancestor is not a return, then this function was not returned at all, so we can exit early
204 const isReturnStatement = (ancestor === null || ancestor === void 0 ? void 0 : ancestor.type) === utils_1.AST_NODE_TYPES.ReturnStatement;
205 const isBodylessArrow = (ancestor === null || ancestor === void 0 ? void 0 : ancestor.type) === utils_1.AST_NODE_TYPES.ArrowFunctionExpression &&
206 ancestor.body.type !== utils_1.AST_NODE_TYPES.BlockStatement;
207 if (!isReturnStatement && !isBodylessArrow) {
208 return false;
209 }
210 while (ancestor) {
211 switch (ancestor.type) {
212 case utils_1.AST_NODE_TYPES.ArrowFunctionExpression:
213 case utils_1.AST_NODE_TYPES.FunctionExpression:
214 case utils_1.AST_NODE_TYPES.FunctionDeclaration:
215 if (ancestor.returnType) {
216 return true;
217 }
218 break;
219 // const x: Foo = () => {};
220 // Assume that a typed variable types the function expression
221 case utils_1.AST_NODE_TYPES.VariableDeclarator:
222 if (ancestor.id.typeAnnotation) {
223 return true;
224 }
225 break;
226 }
227 ancestor = ancestor.parent;
228 }
229 return false;
230}
231exports.ancestorHasReturnType = ancestorHasReturnType;
232//# sourceMappingURL=explicitReturnTypeUtils.js.map
\No newline at end of file