tslint-etc
Version:
More rules for TSLint
115 lines (114 loc) • 4.22 kB
JavaScript
;
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/);
}