UNPKG

tslint-etc

Version:
115 lines (114 loc) 4.22 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.Walker = exports.Rule = void 0; const Lint = require("tslint"); const tsutils = require("tsutils"); const support_1 = require("../support"); class Rule extends Lint.Rules.TypedRule { applyWithProgram(sourceFile, program) { return this.applyWithWalker(new Walker(sourceFile, this.getOptions(), program)); } } exports.Rule = Rule; Rule.metadata = { description: "Enforces the use of `Error` values when throwing or rejecting.", options: null, optionsDescription: "Not configurable.", requiresTypeInfo: true, ruleName: "throw-error", type: "functionality", typescriptOnly: true, }; Rule.FAILURE_STRING = "Throwing non-Error values is forbidden"; class Walker extends Lint.ProgramAwareRuleWalker { constructor() { super(...arguments); this._callbacks = new Map(); this._rejects = new Map(); } visitArrowFunction(node) { let reject = undefined; if (this._callbacks.has(node)) { [, reject] = node.parameters; } if (reject) { this._rejects.set(reject, true); } super.visitArrowFunction(node); if (reject) { this._rejects.delete(reject); } } visitCallExpression(node) { const { arguments: [argument], expression, } = node; const typeChecker = this.getTypeChecker(); if (tsutils.isPropertyAccessExpression(expression)) { const name = expression.name.getText(); const type = typeChecker.getTypeAtLocation(expression.expression); if (name === "reject" && couldBePromise(type)) { let fail = true; if (argument) { const argumentType = typeChecker.getTypeAtLocation(argument); fail = !(support_1.isAny(argumentType) || support_1.couldBeType(argumentType, "Error")); } if (fail) { this.addFailureAtNode(node, Rule.FAILURE_STRING); } } } else if (tsutils.isIdentifier(expression)) { const declaration = support_1.findDeclaration(expression, typeChecker); if (declaration && this._rejects.has(declaration)) { let fail = true; if (argument) { const argumentType = typeChecker.getTypeAtLocation(argument); fail = !(support_1.isAny(argumentType) || support_1.couldBeType(argumentType, "Error")); } if (fail) { this.addFailureAtNode(node, Rule.FAILURE_STRING); } } } super.visitCallExpression(node); } visitFunctionExpression(node) { let reject = undefined; if (this._callbacks.has(node)) { [, reject] = node.parameters; } if (reject) { this._rejects.set(reject, true); } super.visitFunctionExpression(node); if (reject) { this._rejects.delete(reject); } } visitNewExpression(node) { const typeChecker = this.getTypeChecker(); const type = typeChecker.getTypeAtLocation(node.expression); let callback = undefined; if (couldBePromise(type)) { [callback] = node.arguments; } if (callback) { this._callbacks.set(callback, true); } super.visitNewExpression(node); if (callback) { this._callbacks.delete(callback); } } visitThrowStatement(node) { const typeChecker = this.getTypeChecker(); const type = typeChecker.getTypeAtLocation(node.expression); if (!support_1.isAny(type) && !support_1.couldBeType(type, /^(Error|DOMException)$/)) { this.addFailureAtNode(node, Rule.FAILURE_STRING); } super.visitThrowStatement(node); } } exports.Walker = Walker; function couldBePromise(type) { return support_1.couldBeType(type, /^Promise/); }