UNPKG

13.7 kBJavaScriptView Raw
1"use strict";
2var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3 if (k2 === undefined) k2 = k;
4 Object.defineProperty(o, k2, { enumerable: true, get: function() { return m[k]; } });
5}) : (function(o, m, k, k2) {
6 if (k2 === undefined) k2 = k;
7 o[k2] = m[k];
8}));
9var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
10 Object.defineProperty(o, "default", { enumerable: true, value: v });
11}) : function(o, v) {
12 o["default"] = v;
13});
14var __importStar = (this && this.__importStar) || function (mod) {
15 if (mod && mod.__esModule) return mod;
16 var result = {};
17 if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
18 __setModuleDefault(result, mod);
19 return result;
20};
21Object.defineProperty(exports, "__esModule", { value: true });
22const utils_1 = require("@typescript-eslint/utils");
23const tsutils = __importStar(require("tsutils"));
24const util = __importStar(require("../util"));
25const util_1 = require("../util");
26exports.default = util.createRule({
27 name: 'no-unsafe-assignment',
28 meta: {
29 type: 'problem',
30 docs: {
31 description: 'Disallows assigning any to variables and properties',
32 recommended: 'error',
33 requiresTypeChecking: true,
34 },
35 messages: {
36 anyAssignment: 'Unsafe assignment of an `any` value.',
37 anyAssignmentThis: [
38 'Unsafe assignment of an `any` value. `this` is typed as `any`.',
39 'You can try to fix this by turning on the `noImplicitThis` compiler option, or adding a `this` parameter to the function.',
40 ].join('\n'),
41 unsafeArrayPattern: 'Unsafe array destructuring of an `any` array value.',
42 unsafeArrayPatternFromTuple: 'Unsafe array destructuring of a tuple element with an `any` value.',
43 unsafeAssignment: 'Unsafe assignment of type {{sender}} to a variable of type {{receiver}}.',
44 unsafeArraySpread: 'Unsafe spread of an `any` value in an array.',
45 },
46 schema: [],
47 },
48 defaultOptions: [],
49 create(context) {
50 const { program, esTreeNodeToTSNodeMap } = util.getParserServices(context);
51 const checker = program.getTypeChecker();
52 const compilerOptions = program.getCompilerOptions();
53 const isNoImplicitThis = tsutils.isStrictCompilerOptionEnabled(compilerOptions, 'noImplicitThis');
54 // returns true if the assignment reported
55 function checkArrayDestructureHelper(receiverNode, senderNode) {
56 if (receiverNode.type !== utils_1.AST_NODE_TYPES.ArrayPattern) {
57 return false;
58 }
59 const senderTsNode = esTreeNodeToTSNodeMap.get(senderNode);
60 const senderType = checker.getTypeAtLocation(senderTsNode);
61 return checkArrayDestructure(receiverNode, senderType, senderTsNode);
62 }
63 // returns true if the assignment reported
64 function checkArrayDestructure(receiverNode, senderType, senderNode) {
65 // any array
66 // const [x] = ([] as any[]);
67 if (util.isTypeAnyArrayType(senderType, checker)) {
68 context.report({
69 node: receiverNode,
70 messageId: 'unsafeArrayPattern',
71 });
72 return false;
73 }
74 if (!checker.isTupleType(senderType)) {
75 return true;
76 }
77 const tupleElements = util.getTypeArguments(senderType, checker);
78 // tuple with any
79 // const [x] = [1 as any];
80 let didReport = false;
81 for (let receiverIndex = 0; receiverIndex < receiverNode.elements.length; receiverIndex += 1) {
82 const receiverElement = receiverNode.elements[receiverIndex];
83 if (!receiverElement) {
84 continue;
85 }
86 if (receiverElement.type === utils_1.AST_NODE_TYPES.RestElement) {
87 // don't handle rests as they're not a 1:1 assignment
88 continue;
89 }
90 const senderType = tupleElements[receiverIndex];
91 if (!senderType) {
92 continue;
93 }
94 // check for the any type first so we can handle [[[x]]] = [any]
95 if (util.isTypeAnyType(senderType)) {
96 context.report({
97 node: receiverElement,
98 messageId: 'unsafeArrayPatternFromTuple',
99 });
100 // we want to report on every invalid element in the tuple
101 didReport = true;
102 }
103 else if (receiverElement.type === utils_1.AST_NODE_TYPES.ArrayPattern) {
104 didReport = checkArrayDestructure(receiverElement, senderType, senderNode);
105 }
106 else if (receiverElement.type === utils_1.AST_NODE_TYPES.ObjectPattern) {
107 didReport = checkObjectDestructure(receiverElement, senderType, senderNode);
108 }
109 }
110 return didReport;
111 }
112 // returns true if the assignment reported
113 function checkObjectDestructureHelper(receiverNode, senderNode) {
114 if (receiverNode.type !== utils_1.AST_NODE_TYPES.ObjectPattern) {
115 return false;
116 }
117 const senderTsNode = esTreeNodeToTSNodeMap.get(senderNode);
118 const senderType = checker.getTypeAtLocation(senderTsNode);
119 return checkObjectDestructure(receiverNode, senderType, senderTsNode);
120 }
121 // returns true if the assignment reported
122 function checkObjectDestructure(receiverNode, senderType, senderNode) {
123 const properties = new Map(senderType
124 .getProperties()
125 .map(property => [
126 property.getName(),
127 checker.getTypeOfSymbolAtLocation(property, senderNode),
128 ]));
129 let didReport = false;
130 for (let receiverIndex = 0; receiverIndex < receiverNode.properties.length; receiverIndex += 1) {
131 const receiverProperty = receiverNode.properties[receiverIndex];
132 if (receiverProperty.type === utils_1.AST_NODE_TYPES.RestElement) {
133 // don't bother checking rest
134 continue;
135 }
136 let key;
137 if (receiverProperty.computed === false) {
138 key =
139 receiverProperty.key.type === utils_1.AST_NODE_TYPES.Identifier
140 ? receiverProperty.key.name
141 : String(receiverProperty.key.value);
142 }
143 else if (receiverProperty.key.type === utils_1.AST_NODE_TYPES.Literal) {
144 key = String(receiverProperty.key.value);
145 }
146 else if (receiverProperty.key.type === utils_1.AST_NODE_TYPES.TemplateLiteral &&
147 receiverProperty.key.quasis.length === 1) {
148 key = String(receiverProperty.key.quasis[0].value.cooked);
149 }
150 else {
151 // can't figure out the name, so skip it
152 continue;
153 }
154 const senderType = properties.get(key);
155 if (!senderType) {
156 continue;
157 }
158 // check for the any type first so we can handle {x: {y: z}} = {x: any}
159 if (util.isTypeAnyType(senderType)) {
160 context.report({
161 node: receiverProperty.value,
162 messageId: 'unsafeArrayPatternFromTuple',
163 });
164 didReport = true;
165 }
166 else if (receiverProperty.value.type === utils_1.AST_NODE_TYPES.ArrayPattern) {
167 didReport = checkArrayDestructure(receiverProperty.value, senderType, senderNode);
168 }
169 else if (receiverProperty.value.type === utils_1.AST_NODE_TYPES.ObjectPattern) {
170 didReport = checkObjectDestructure(receiverProperty.value, senderType, senderNode);
171 }
172 }
173 return didReport;
174 }
175 // returns true if the assignment reported
176 function checkAssignment(receiverNode, senderNode, reportingNode, comparisonType) {
177 var _a;
178 const receiverTsNode = esTreeNodeToTSNodeMap.get(receiverNode);
179 const receiverType = comparisonType === 2 /* Contextual */
180 ? (_a = util.getContextualType(checker, receiverTsNode)) !== null && _a !== void 0 ? _a : checker.getTypeAtLocation(receiverTsNode)
181 : checker.getTypeAtLocation(receiverTsNode);
182 const senderType = checker.getTypeAtLocation(esTreeNodeToTSNodeMap.get(senderNode));
183 if (util.isTypeAnyType(senderType)) {
184 // handle cases when we assign any ==> unknown.
185 if (util.isTypeUnknownType(receiverType)) {
186 return false;
187 }
188 let messageId = 'anyAssignment';
189 if (!isNoImplicitThis) {
190 // `var foo = this`
191 const thisExpression = (0, util_1.getThisExpression)(senderNode);
192 if (thisExpression &&
193 util.isTypeAnyType(util.getConstrainedTypeAtLocation(checker, esTreeNodeToTSNodeMap.get(thisExpression)))) {
194 messageId = 'anyAssignmentThis';
195 }
196 }
197 context.report({
198 node: reportingNode,
199 messageId,
200 });
201 return true;
202 }
203 if (comparisonType === 0 /* None */) {
204 return false;
205 }
206 const result = util.isUnsafeAssignment(senderType, receiverType, checker, senderNode);
207 if (!result) {
208 return false;
209 }
210 const { sender, receiver } = result;
211 context.report({
212 node: reportingNode,
213 messageId: 'unsafeAssignment',
214 data: {
215 sender: checker.typeToString(sender),
216 receiver: checker.typeToString(receiver),
217 },
218 });
219 return true;
220 }
221 function getComparisonType(typeAnnotation) {
222 return typeAnnotation
223 ? // if there's a type annotation, we can do a comparison
224 1 /* Basic */
225 : // no type annotation means the variable's type will just be inferred, thus equal
226 0 /* None */;
227 }
228 return {
229 'VariableDeclarator[init != null]'(node) {
230 const init = util.nullThrows(node.init, util.NullThrowsReasons.MissingToken(node.type, 'init'));
231 let didReport = checkAssignment(node.id, init, node, getComparisonType(node.id.typeAnnotation));
232 if (!didReport) {
233 didReport = checkArrayDestructureHelper(node.id, init);
234 }
235 if (!didReport) {
236 checkObjectDestructureHelper(node.id, init);
237 }
238 },
239 'PropertyDefinition[value != null]'(node) {
240 checkAssignment(node.key, node.value, node, getComparisonType(node.typeAnnotation));
241 },
242 'AssignmentExpression[operator = "="], AssignmentPattern'(node) {
243 let didReport = checkAssignment(node.left, node.right, node, 1 /* Basic */);
244 if (!didReport) {
245 didReport = checkArrayDestructureHelper(node.left, node.right);
246 }
247 if (!didReport) {
248 checkObjectDestructureHelper(node.left, node.right);
249 }
250 },
251 // object pattern props are checked via assignments
252 ':not(ObjectPattern) > Property'(node) {
253 if (node.value.type === utils_1.AST_NODE_TYPES.AssignmentPattern ||
254 node.value.type === utils_1.AST_NODE_TYPES.TSEmptyBodyFunctionExpression) {
255 // handled by other selector
256 return;
257 }
258 checkAssignment(node.key, node.value, node, 2 /* Contextual */);
259 },
260 'ArrayExpression > SpreadElement'(node) {
261 const resetNode = esTreeNodeToTSNodeMap.get(node.argument);
262 const restType = checker.getTypeAtLocation(resetNode);
263 if (util.isTypeAnyType(restType) ||
264 util.isTypeAnyArrayType(restType, checker)) {
265 context.report({
266 node: node,
267 messageId: 'unsafeArraySpread',
268 });
269 }
270 },
271 'JSXAttribute[value != null]'(node) {
272 const value = util.nullThrows(node.value, util.NullThrowsReasons.MissingToken(node.type, 'value'));
273 if (value.type !== utils_1.AST_NODE_TYPES.JSXExpressionContainer ||
274 value.expression.type === utils_1.AST_NODE_TYPES.JSXEmptyExpression) {
275 return;
276 }
277 checkAssignment(node.name, value.expression, value.expression, 2 /* Contextual */);
278 },
279 };
280 },
281});
282//# sourceMappingURL=no-unsafe-assignment.js.map
\No newline at end of file