個人的にはUnhandled Promise Rejectionでnodeプロセスを終了させたくない場合は
process.on('unhandledRejection')
イベントをハンドルすると良いと思う。
それとは別に、意図しない処理を洗い出すのに役立つのでUnhandledなPromiseがあったら見つけるESLintルールを用意してみた。
const ts = require('typescript');
module.exports = {
meta: {
type: "problem",
docs: {
description: "Ensure promises are handled with `await` or `catch`",
category: "Possible Errors",
recommended: true,
},
schema: [],
messages: {
unhandledPromise:
"Promises must be handled with `await` or `.catch()`.",
},
},
create(context) {
const tc = context.parserServices.program.getTypeChecker();
function isTypePromise(t, sym, visited = new Set()) {
if (visited.has(t)) return false;
visited.add(t);
const symbol = t.getSymbol();
if (symbol === sym) return true;
if (t.isUnionOrIntersection()) {
return t.types.some(subType =>
isTypePromise(subType, sym, visited));
}
const baseTypes = t.getBaseTypes?.();
if (baseTypes && baseTypes.some(base =>
isTypePromise(base, sym, visited))) {
return true;
}
const typeArgs = t.typeArguments;
if (typeArgs && typeArgs.some(arg =>
isTypePromise(arg, sym, visited))) {
return true;
}
return false;
}
function isPromise(node) {
const tsNode = context.parserServices.esTreeNodeToTSNodeMap.get(node);
const type = tc.getTypeAtLocation(tsNode);
const promiseSymbol = tc.resolveName("Promise",
tsNode.getSourceFile(), ts.SymbolFlags.Interface, false);
if (!promiseSymbol) {
return false;
}
return isTypePromise(type, promiseSymbol);
}
function hasCatchInChain(callNode) {
let currentCall = callNode;
while (
currentCall &&
currentCall.type === "CallExpression" &&
currentCall.callee.type === "MemberExpression"
) {
const methodName = currentCall.callee.property.name;
if (methodName === "catch") return true;
if (currentCall.callee.object.type === "CallExpression") {
currentCall = currentCall.callee.object;
} else {
break;
}
}
return false;
}
return {
ExpressionStatement(node) {
const { expression } = node;
if (
expression.type === "CallExpression" &&
context.parserServices &&
context.parserServices.program
) {
if (isPromise(expression)) {
if (node.parent && node.parent.type === "AwaitExpression") {
return;
}
if (!hasCatchInChain(expression)) {
context.report({
node,
messageId: "unhandledPromise",
});
}
}
}
},
};
},
},