@typescript-eslint/eslint-plugin
Version:
TypeScript plugin for ESLint
117 lines (116 loc) • 4.8 kB
JavaScript
Object.defineProperty(exports, "__esModule", { value: true });
const utils_1 = require("@typescript-eslint/utils");
const util_1 = require("../util");
exports.default = (0, util_1.createRule)({
name: 'prefer-promise-reject-errors',
meta: {
type: 'suggestion',
docs: {
description: 'Require using Error objects as Promise rejection reasons',
extendsBaseRule: true,
recommended: 'recommended',
requiresTypeChecking: true,
},
messages: {
rejectAnError: 'Expected the Promise rejection reason to be an Error.',
},
schema: [
{
type: 'object',
additionalProperties: false,
properties: {
allowEmptyReject: {
type: 'boolean',
description: 'Whether to allow calls to `Promise.reject()` with no arguments.',
},
allowThrowingAny: {
type: 'boolean',
description: 'Whether to always allow throwing values typed as `any`.',
},
allowThrowingUnknown: {
type: 'boolean',
description: 'Whether to always allow throwing values typed as `unknown`.',
},
},
},
],
},
defaultOptions: [
{
allowEmptyReject: false,
allowThrowingAny: false,
allowThrowingUnknown: false,
},
],
create(context, [options]) {
const services = (0, util_1.getParserServices)(context);
function checkRejectCall(callExpression) {
const argument = callExpression.arguments.at(0);
if (argument) {
const type = services.getTypeAtLocation(argument);
if (options.allowThrowingAny && (0, util_1.isTypeAnyType)(type)) {
return;
}
if (options.allowThrowingUnknown && (0, util_1.isTypeUnknownType)(type)) {
return;
}
if ((0, util_1.isErrorLike)(services.program, type) ||
(0, util_1.isReadonlyErrorLike)(services.program, type)) {
return;
}
}
else if (options.allowEmptyReject) {
return;
}
context.report({
node: callExpression,
messageId: 'rejectAnError',
});
}
function typeAtLocationIsLikePromise(node) {
const type = services.getTypeAtLocation(node);
return ((0, util_1.isPromiseConstructorLike)(services.program, type) ||
(0, util_1.isPromiseLike)(services.program, type));
}
return {
CallExpression(node) {
const callee = (0, util_1.skipChainExpression)(node.callee);
if (callee.type !== utils_1.AST_NODE_TYPES.MemberExpression) {
return;
}
if (!(0, util_1.isStaticMemberAccessOfValue)(callee, context, 'reject') ||
!typeAtLocationIsLikePromise(callee.object)) {
return;
}
checkRejectCall(node);
},
NewExpression(node) {
const callee = (0, util_1.skipChainExpression)(node.callee);
if (!(0, util_1.isPromiseConstructorLike)(services.program, services.getTypeAtLocation(callee))) {
return;
}
const executor = node.arguments.at(0);
if (!executor || !(0, util_1.isFunction)(executor)) {
return;
}
const rejectParamNode = executor.params.at(1);
if (!rejectParamNode || !(0, util_1.isIdentifier)(rejectParamNode)) {
return;
}
// reject param is always present in variables declared by executor
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
const rejectVariable = context.sourceCode
.getDeclaredVariables(executor)
.find(variable => variable.identifiers.includes(rejectParamNode));
rejectVariable.references.forEach(ref => {
if (ref.identifier.parent.type !== utils_1.AST_NODE_TYPES.CallExpression ||
ref.identifier !== ref.identifier.parent.callee) {
return;
}
checkRejectCall(ref.identifier.parent);
});
},
};
},
});
;