📄 only-throw-error¶
📊 Analysis Summary¶
| Metric | Count |
|---|---|
| 🔧 Functions | 3 |
| 📦 Imports | 15 |
| 📑 Type Aliases | 2 |
📚 Table of Contents¶
🛠️ File Location:¶
📂 packages/eslint-plugin/src/rules/only-throw-error.ts
📤 Default Export¶
| Property | Value |
|---|---|
name |
'only-throw-error' |
meta.type |
'problem' |
meta.docs.description |
'Disallow throwing non-Error values as exceptions' |
meta.docs.extendsBaseRule |
'no-throw-literal' |
meta.docs.recommended |
'recommended' |
meta.docs.requiresTypeChecking |
true |
meta.messages.object |
'Expected an error object to be thrown.' |
meta.messages.undef |
'Do not throw undefined.' |
meta.schema |
[ { type: 'object', additionalProperties: false, properties: { allow: { ...typeOrValueSpecifiersSchema, description: ... |
defaultOptions |
[ { allow: [], allowRethrowing: true, allowThrowingAny: true, allowThrowingUnknown: true, }, ] |
Entry point: create — documented under Functions.
📦 Imports¶
| Name | Source |
|---|---|
TSESTree |
@typescript-eslint/utils |
AST_NODE_TYPES |
@typescript-eslint/utils |
isThenableType |
ts-api-utils |
TypeOrValueSpecifier |
../util |
createRule |
../util |
findVariable |
../util |
getParserServices |
../util |
isErrorLike |
../util |
isTypeAnyType |
../util |
isTypeUnknownType |
../util |
typeMatchesSomeSpecifier |
../util |
typeOrValueSpecifiersSchema |
../util |
nullThrows |
../util |
parseCatchCall |
../util/promiseUtils |
parseThenCall |
../util/promiseUtils |
Functions¶
create(context: any, [options]: any): { ThrowStatement(node: any): void; }¶
Parameters:
contextany[options]any
Returns: { ThrowStatement(node: any): void; }
Calls:
getParserServices (from ../util)context.sourceCode.getScopenullThrows (from ../util)findVariable (from ../util)smVariable.defs.filterparseCatchCall (from ../util/promiseUtils)parseThenCall (from ../util/promiseUtils)services.esTreeNodeToTSNodeMap.getisThenableType (from ts-api-utils)services.program.getTypeCheckerisRethrownErrorservices.getTypeAtLocationtypeMatchesSomeSpecifier (from ../util)tsutils.isTypeFlagSetcontext.reportisTypeAnyType (from ../util)isTypeUnknownType (from ../util)isErrorLike (from ../util)checkThrowArgument
Internal Comments:
// try { /* ... */ } catch (x) { throw x; }
// promise.catch(x => { throw x; })
// promise.then(onFulfilled, x => { throw x; })
// make sure we're actually dealing with a promise
Code
create(context, [options]) {
const services = getParserServices(context);
const allow = options.allow;
function isRethrownError(node: TSESTree.Node): boolean {
if (node.type !== AST_NODE_TYPES.Identifier) {
return false;
}
const scope = context.sourceCode.getScope(node);
const smVariable = nullThrows(
findVariable(scope, node),
`Variable ${node.name} should exist in scope manager`,
);
const variableDefinitions = smVariable.defs.filter(
def => def.isVariableDefinition,
);
if (variableDefinitions.length !== 1) {
return false;
}
const def = smVariable.defs[0];
// try { /* ... */ } catch (x) { throw x; }
if (def.node.type === AST_NODE_TYPES.CatchClause) {
return true;
}
// promise.catch(x => { throw x; })
// promise.then(onFulfilled, x => { throw x; })
if (
def.node.type === AST_NODE_TYPES.ArrowFunctionExpression &&
def.node.params.length >= 1 &&
def.node.params[0] === def.name &&
def.node.parent.type === AST_NODE_TYPES.CallExpression
) {
const callExpression = def.node.parent;
const parsedPromiseHandlingCall =
parseCatchCall(callExpression, context) ??
parseThenCall(callExpression, context);
if (parsedPromiseHandlingCall != null) {
const { object, onRejected } = parsedPromiseHandlingCall;
if (onRejected === def.node) {
const tsObjectNode = services.esTreeNodeToTSNodeMap.get(
object,
) as ts.Expression;
// make sure we're actually dealing with a promise
if (
isThenableType(services.program.getTypeChecker(), tsObjectNode)
) {
return true;
}
}
}
}
return false;
}
function checkThrowArgument(node: TSESTree.Node): void {
if (options.allowRethrowing && isRethrownError(node)) {
return;
}
const type = services.getTypeAtLocation(node);
if (typeMatchesSomeSpecifier(type, allow, services.program)) {
return;
}
if (tsutils.isTypeFlagSet(type, ts.TypeFlags.Undefined)) {
context.report({ node, messageId: 'undef' });
return;
}
if (options.allowThrowingAny && isTypeAnyType(type)) {
return;
}
if (options.allowThrowingUnknown && isTypeUnknownType(type)) {
return;
}
if (isErrorLike(services.program, type)) {
return;
}
context.report({ node, messageId: 'object' });
}
return {
ThrowStatement(node): void {
checkThrowArgument(node.argument);
},
};
}
Internal helpers¶
Declared inside another function in this file.
isRethrownError(node: TSESTree.Node): boolean¶
Parameters:
nodeTSESTree.Node
Returns: boolean
Calls:
context.sourceCode.getScopenullThrows (from ../util)findVariable (from ../util)smVariable.defs.filterparseCatchCall (from ../util/promiseUtils)parseThenCall (from ../util/promiseUtils)services.esTreeNodeToTSNodeMap.getisThenableType (from ts-api-utils)services.program.getTypeChecker
Internal Comments:
// try { /* ... */ } catch (x) { throw x; }
// promise.catch(x => { throw x; })
// promise.then(onFulfilled, x => { throw x; })
// make sure we're actually dealing with a promise
Code
function isRethrownError(node: TSESTree.Node): boolean {
if (node.type !== AST_NODE_TYPES.Identifier) {
return false;
}
const scope = context.sourceCode.getScope(node);
const smVariable = nullThrows(
findVariable(scope, node),
`Variable ${node.name} should exist in scope manager`,
);
const variableDefinitions = smVariable.defs.filter(
def => def.isVariableDefinition,
);
if (variableDefinitions.length !== 1) {
return false;
}
const def = smVariable.defs[0];
// try { /* ... */ } catch (x) { throw x; }
if (def.node.type === AST_NODE_TYPES.CatchClause) {
return true;
}
// promise.catch(x => { throw x; })
// promise.then(onFulfilled, x => { throw x; })
if (
def.node.type === AST_NODE_TYPES.ArrowFunctionExpression &&
def.node.params.length >= 1 &&
def.node.params[0] === def.name &&
def.node.parent.type === AST_NODE_TYPES.CallExpression
) {
const callExpression = def.node.parent;
const parsedPromiseHandlingCall =
parseCatchCall(callExpression, context) ??
parseThenCall(callExpression, context);
if (parsedPromiseHandlingCall != null) {
const { object, onRejected } = parsedPromiseHandlingCall;
if (onRejected === def.node) {
const tsObjectNode = services.esTreeNodeToTSNodeMap.get(
object,
) as ts.Expression;
// make sure we're actually dealing with a promise
if (
isThenableType(services.program.getTypeChecker(), tsObjectNode)
) {
return true;
}
}
}
}
return false;
}
checkThrowArgument(node: TSESTree.Node): void¶
Parameters:
nodeTSESTree.Node
Returns: void
Calls:
isRethrownErrorservices.getTypeAtLocationtypeMatchesSomeSpecifier (from ../util)tsutils.isTypeFlagSetcontext.reportisTypeAnyType (from ../util)isTypeUnknownType (from ../util)isErrorLike (from ../util)
Code
function checkThrowArgument(node: TSESTree.Node): void {
if (options.allowRethrowing && isRethrownError(node)) {
return;
}
const type = services.getTypeAtLocation(node);
if (typeMatchesSomeSpecifier(type, allow, services.program)) {
return;
}
if (tsutils.isTypeFlagSet(type, ts.TypeFlags.Undefined)) {
context.report({ node, messageId: 'undef' });
return;
}
if (options.allowThrowingAny && isTypeAnyType(type)) {
return;
}
if (options.allowThrowingUnknown && isTypeUnknownType(type)) {
return;
}
if (isErrorLike(services.program, type)) {
return;
}
context.report({ node, messageId: 'object' });
}
Type Aliases¶
MessageIds¶
Options¶
type Options = [
{
allow?: TypeOrValueSpecifier[];
allowRethrowing?: boolean;
allowThrowingAny?: boolean;
allowThrowingUnknown?: boolean;
},
];
Generated by Syntax Scribe