UNPKG

eslint-plugin-etc

Version:
154 lines (153 loc) 5.92 kB
"use strict"; const experimental_utils_1 = require("@typescript-eslint/experimental-utils"); const eslint_etc_1 = require("eslint-etc"); const utils_1 = require("../utils"); function isParenthesised(sourceCode, node) { const before = sourceCode.getTokenBefore(node); const after = sourceCode.getTokenAfter(node); return (before && after && before.value === "(" && before.range[1] <= node.range[0] && after.value === ")" && after.range[0] >= node.range[1]); } const defaultOptions = []; const rule = (0, utils_1.ruleCreator)({ defaultOptions, meta: { docs: { description: "Forbids implicit `any` error parameters in promise rejections.", recommended: false, suggestion: true, }, fixable: "code", hasSuggestions: true, messages: { explicitAny: "Explicit `any` in promise rejection.", implicitAny: "Implicit `any` in promise rejection.", narrowed: "Error type must be `unknown` or `any`.", suggestExplicitUnknown: "Use `unknown` instead, this will force you to explicitly and safely assert the type is correct.", }, schema: [ { additionalProperties: false, properties: { allowExplicitAny: { type: "boolean", }, }, type: "object", }, ], type: "suggestion", }, name: "no-implicit-any-catch", create: (context, unused) => { const { couldBeType } = (0, eslint_etc_1.getTypeServices)(context); const [config = {}] = context.options; const { allowExplicitAny = false } = config; const sourceCode = context.getSourceCode(); function checkRejectionCallback(callExpression, callback) { if (!(0, eslint_etc_1.isArrowFunctionExpression)(callback) && !(0, eslint_etc_1.isFunctionExpression)(callback)) { return; } const [param] = callback.params; if (!param) { return; } if ((0, eslint_etc_1.hasTypeAnnotation)(param)) { const { typeAnnotation } = param; const { typeAnnotation: { type }, } = typeAnnotation; if (type === experimental_utils_1.AST_NODE_TYPES.TSAnyKeyword) { if (allowExplicitAny) { return; } if (!isPromiseCall(callExpression)) { return; } function fix(fixer) { return fixer.replaceText(typeAnnotation, ": unknown"); } context.report({ fix, messageId: "explicitAny", node: param, suggest: [ { messageId: "suggestExplicitUnknown", fix, }, ], }); } else if (type !== experimental_utils_1.AST_NODE_TYPES.TSUnknownKeyword) { if (!isPromiseCall(callExpression)) { return; } function fix(fixer) { return fixer.replaceText(typeAnnotation, ": unknown"); } context.report({ messageId: "narrowed", node: param, suggest: [ { messageId: "suggestExplicitUnknown", fix, }, ], }); } } else { if (!isPromiseCall(callExpression)) { return; } function fix(fixer) { if (isParenthesised(sourceCode, param)) { return fixer.insertTextAfter(param, ": unknown"); } return [ fixer.insertTextBefore(param, "("), fixer.insertTextAfter(param, ": unknown)"), ]; } context.report({ fix, messageId: "implicitAny", node: param, suggest: [ { messageId: "suggestExplicitUnknown", fix, }, ], }); } } function isPromiseCall(callExpression) { const { callee } = callExpression; if (!(0, eslint_etc_1.isMemberExpression)(callee)) { return false; } return couldBeType(callee.object, "Promise"); } return { "CallExpression[callee.property.name='catch']": (callExpression) => { const [callback] = callExpression.arguments; if (callback) { checkRejectionCallback(callExpression, callback); } }, "CallExpression[callee.property.name='then']": (callExpression) => { const [, callback] = callExpression.arguments; if (callback) { checkRejectionCallback(callExpression, callback); } }, }; }, }); module.exports = rule;