| "use strict"; |
| var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { |
| if (k2 === undefined) k2 = k; |
| Object.defineProperty(o, k2, { enumerable: true, get: function() { return m[k]; } }); |
| }) : (function(o, m, k, k2) { |
| if (k2 === undefined) k2 = k; |
| o[k2] = m[k]; |
| })); |
| var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) { |
| Object.defineProperty(o, "default", { enumerable: true, value: v }); |
| }) : function(o, v) { |
| o["default"] = v; |
| }); |
| var __importStar = (this && this.__importStar) || function (mod) { |
| if (mod && mod.__esModule) return mod; |
| var result = {}; |
| if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k); |
| __setModuleDefault(result, mod); |
| return result; |
| }; |
| Object.defineProperty(exports, "__esModule", { value: true }); |
| const experimental_utils_1 = require("@typescript-eslint/experimental-utils"); |
| const util = __importStar(require("../util")); |
| exports.default = util.createRule({ |
| name: 'no-unsafe-assignment', |
| meta: { |
| type: 'problem', |
| docs: { |
| description: 'Disallows assigning any to variables and properties', |
| category: 'Possible Errors', |
| recommended: 'error', |
| requiresTypeChecking: true, |
| }, |
| messages: { |
| anyAssignment: 'Unsafe assignment of an any value.', |
| unsafeArrayPattern: 'Unsafe array destructuring of an any array value.', |
| unsafeArrayPatternFromTuple: 'Unsafe array destructuring of a tuple element with an any value.', |
| unsafeAssignment: 'Unsafe assignment of type {{sender}} to a variable of type {{receiver}}.', |
| unsafeArraySpread: 'Unsafe spread of an any value in an array.', |
| }, |
| schema: [], |
| }, |
| defaultOptions: [], |
| create(context) { |
| const { program, esTreeNodeToTSNodeMap } = util.getParserServices(context); |
| const checker = program.getTypeChecker(); |
| // returns true if the assignment reported |
| function checkArrayDestructureHelper(receiverNode, senderNode) { |
| if (receiverNode.type !== experimental_utils_1.AST_NODE_TYPES.ArrayPattern) { |
| return false; |
| } |
| const senderTsNode = esTreeNodeToTSNodeMap.get(senderNode); |
| const senderType = checker.getTypeAtLocation(senderTsNode); |
| return checkArrayDestructure(receiverNode, senderType, senderTsNode); |
| } |
| // returns true if the assignment reported |
| function checkArrayDestructure(receiverNode, senderType, senderNode) { |
| // any array |
| // const [x] = ([] as any[]); |
| if (util.isTypeAnyArrayType(senderType, checker)) { |
| context.report({ |
| node: receiverNode, |
| messageId: 'unsafeArrayPattern', |
| }); |
| return false; |
| } |
| if (!checker.isTupleType(senderType)) { |
| return true; |
| } |
| const tupleElements = util.getTypeArguments(senderType, checker); |
| // tuple with any |
| // const [x] = [1 as any]; |
| let didReport = false; |
| for (let receiverIndex = 0; receiverIndex < receiverNode.elements.length; receiverIndex += 1) { |
| const receiverElement = receiverNode.elements[receiverIndex]; |
| if (!receiverElement) { |
| continue; |
| } |
| if (receiverElement.type === experimental_utils_1.AST_NODE_TYPES.RestElement) { |
| // don't handle rests as they're not a 1:1 assignment |
| continue; |
| } |
| const senderType = tupleElements[receiverIndex]; |
| if (!senderType) { |
| continue; |
| } |
| // check for the any type first so we can handle [[[x]]] = [any] |
| if (util.isTypeAnyType(senderType)) { |
| context.report({ |
| node: receiverElement, |
| messageId: 'unsafeArrayPatternFromTuple', |
| }); |
| // we want to report on every invalid element in the tuple |
| didReport = true; |
| } |
| else if (receiverElement.type === experimental_utils_1.AST_NODE_TYPES.ArrayPattern) { |
| didReport = checkArrayDestructure(receiverElement, senderType, senderNode); |
| } |
| else if (receiverElement.type === experimental_utils_1.AST_NODE_TYPES.ObjectPattern) { |
| didReport = checkObjectDestructure(receiverElement, senderType, senderNode); |
| } |
| } |
| return didReport; |
| } |
| // returns true if the assignment reported |
| function checkObjectDestructureHelper(receiverNode, senderNode) { |
| if (receiverNode.type !== experimental_utils_1.AST_NODE_TYPES.ObjectPattern) { |
| return false; |
| } |
| const senderTsNode = esTreeNodeToTSNodeMap.get(senderNode); |
| const senderType = checker.getTypeAtLocation(senderTsNode); |
| return checkObjectDestructure(receiverNode, senderType, senderTsNode); |
| } |
| // returns true if the assignment reported |
| function checkObjectDestructure(receiverNode, senderType, senderNode) { |
| const properties = new Map(senderType |
| .getProperties() |
| .map(property => [ |
| property.getName(), |
| checker.getTypeOfSymbolAtLocation(property, senderNode), |
| ])); |
| let didReport = false; |
| for (let receiverIndex = 0; receiverIndex < receiverNode.properties.length; receiverIndex += 1) { |
| const receiverProperty = receiverNode.properties[receiverIndex]; |
| if (receiverProperty.type === experimental_utils_1.AST_NODE_TYPES.RestElement) { |
| // don't bother checking rest |
| continue; |
| } |
| let key; |
| if (receiverProperty.computed === false) { |
| key = |
| receiverProperty.key.type === experimental_utils_1.AST_NODE_TYPES.Identifier |
| ? receiverProperty.key.name |
| : String(receiverProperty.key.value); |
| } |
| else if (receiverProperty.key.type === experimental_utils_1.AST_NODE_TYPES.Literal) { |
| key = String(receiverProperty.key.value); |
| } |
| else if (receiverProperty.key.type === experimental_utils_1.AST_NODE_TYPES.TemplateLiteral && |
| receiverProperty.key.quasis.length === 1) { |
| key = String(receiverProperty.key.quasis[0].value.cooked); |
| } |
| else { |
| // can't figure out the name, so skip it |
| continue; |
| } |
| const senderType = properties.get(key); |
| if (!senderType) { |
| continue; |
| } |
| // check for the any type first so we can handle {x: {y: z}} = {x: any} |
| if (util.isTypeAnyType(senderType)) { |
| context.report({ |
| node: receiverProperty.value, |
| messageId: 'unsafeArrayPatternFromTuple', |
| }); |
| didReport = true; |
| } |
| else if (receiverProperty.value.type === experimental_utils_1.AST_NODE_TYPES.ArrayPattern) { |
| didReport = checkArrayDestructure(receiverProperty.value, senderType, senderNode); |
| } |
| else if (receiverProperty.value.type === experimental_utils_1.AST_NODE_TYPES.ObjectPattern) { |
| didReport = checkObjectDestructure(receiverProperty.value, senderType, senderNode); |
| } |
| } |
| return didReport; |
| } |
| // returns true if the assignment reported |
| function checkAssignment(receiverNode, senderNode, reportingNode, comparisonType) { |
| var _a; |
| const receiverTsNode = esTreeNodeToTSNodeMap.get(receiverNode); |
| const receiverType = comparisonType === 2 /* Contextual */ |
| ? (_a = util.getContextualType(checker, receiverTsNode)) !== null && _a !== void 0 ? _a : checker.getTypeAtLocation(receiverTsNode) : checker.getTypeAtLocation(receiverTsNode); |
| const senderType = checker.getTypeAtLocation(esTreeNodeToTSNodeMap.get(senderNode)); |
| if (util.isTypeAnyType(senderType)) { |
| // handle cases when we assign any ==> unknown. |
| if (util.isTypeUnknownType(receiverType)) { |
| return false; |
| } |
| context.report({ |
| node: reportingNode, |
| messageId: 'anyAssignment', |
| }); |
| return true; |
| } |
| if (comparisonType === 0 /* None */) { |
| return false; |
| } |
| const result = util.isUnsafeAssignment(senderType, receiverType, checker); |
| if (!result) { |
| return false; |
| } |
| const { sender, receiver } = result; |
| context.report({ |
| node: reportingNode, |
| messageId: 'unsafeAssignment', |
| data: { |
| sender: checker.typeToString(sender), |
| receiver: checker.typeToString(receiver), |
| }, |
| }); |
| return true; |
| } |
| function getComparisonType(typeAnnotation) { |
| return typeAnnotation |
| ? // if there's a type annotation, we can do a comparison |
| 1 /* Basic */ |
| : // no type annotation means the variable's type will just be inferred, thus equal |
| 0 /* None */; |
| } |
| return { |
| 'VariableDeclarator[init != null]'(node) { |
| const init = util.nullThrows(node.init, util.NullThrowsReasons.MissingToken(node.type, 'init')); |
| let didReport = checkAssignment(node.id, init, node, getComparisonType(node.id.typeAnnotation)); |
| if (!didReport) { |
| didReport = checkArrayDestructureHelper(node.id, init); |
| } |
| if (!didReport) { |
| checkObjectDestructureHelper(node.id, init); |
| } |
| }, |
| 'ClassProperty[value != null]'(node) { |
| checkAssignment(node.key, node.value, node, getComparisonType(node.typeAnnotation)); |
| }, |
| 'AssignmentExpression[operator = "="], AssignmentPattern'(node) { |
| let didReport = checkAssignment(node.left, node.right, node, 1 /* Basic */); |
| if (!didReport) { |
| didReport = checkArrayDestructureHelper(node.left, node.right); |
| } |
| if (!didReport) { |
| checkObjectDestructureHelper(node.left, node.right); |
| } |
| }, |
| // object pattern props are checked via assignments |
| ':not(ObjectPattern) > Property'(node) { |
| if (node.value.type === experimental_utils_1.AST_NODE_TYPES.AssignmentPattern || |
| node.value.type === experimental_utils_1.AST_NODE_TYPES.TSEmptyBodyFunctionExpression) { |
| // handled by other selector |
| return; |
| } |
| checkAssignment(node.key, node.value, node, 2 /* Contextual */); |
| }, |
| 'ArrayExpression > SpreadElement'(node) { |
| const resetNode = esTreeNodeToTSNodeMap.get(node.argument); |
| const restType = checker.getTypeAtLocation(resetNode); |
| if (util.isTypeAnyType(restType) || |
| util.isTypeAnyArrayType(restType, checker)) { |
| context.report({ |
| node: node, |
| messageId: 'unsafeArraySpread', |
| }); |
| } |
| }, |
| 'JSXAttribute[value != null]'(node) { |
| const value = util.nullThrows(node.value, util.NullThrowsReasons.MissingToken(node.type, 'value')); |
| if (value.type !== experimental_utils_1.AST_NODE_TYPES.JSXExpressionContainer || |
| value.expression.type === experimental_utils_1.AST_NODE_TYPES.JSXEmptyExpression) { |
| return; |
| } |
| checkAssignment(node.name, value.expression, value.expression, 2 /* Contextual */); |
| }, |
| }; |
| }, |
| }); |
| //# sourceMappingURL=no-unsafe-assignment.js.map |