npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@mkvisuals/eslint-plugin-throws

v0.2.1

Published

ESLint plugin that brings checked exception awareness to JavaScript/TypeScript via @throws JSDoc tags

Readme

@mkvisuals/eslint-plugin-throws

Bring Java/PHP-style checked exception awareness to JavaScript and TypeScript via @throws JSDoc tags.

When a function is annotated with @throws, every call site must either be wrapped in a try/catch block or the calling function must itself declare @throws (propagation). The plugin also optionally enforces that any function containing a throw statement has a @throws annotation.

Works with ESLint 8 (legacy eslintrc) and ESLint 9 (flat config). No external dependencies.

Installation

npm install --save-dev @mkvisuals/eslint-plugin-throws

Usage

ESLint 9 — flat config

// eslint.config.js
import throws from '@mkvisuals/eslint-plugin-throws';

export default [
  // Use the recommended preset (warn on uncaught @throws call sites)
  ...throws.configs.recommended,
];

Or with the strict preset (also requires @throws on any function that throws):

export default [
  ...throws.configs.strict,
];

Or configure manually with all options:

import throws from '@mkvisuals/eslint-plugin-throws';

export default [
  {
    plugins: { throws },
    rules: {
      'throws/no-uncaught-throws': ['warn', {
        requireThrowsAnnotation: true,
        removeUnnecessaryThrows: true,
        fixStrategy: 'propagate',
      }],
    },
  },
];

ESLint 8 — legacy eslintrc

// .eslintrc.cjs
module.exports = {
  plugins: ['@mkvisuals/eslint-plugin-throws'],
  rules: {
    'throws/no-uncaught-throws': 'warn',
    // or with all options:
    // 'throws/no-uncaught-throws': ['warn', {
    //   requireThrowsAnnotation: true,
    //   removeUnnecessaryThrows: true,
    //   fixStrategy: 'propagate',
    // }],
  },
};

TypeScript — cross-file resolution

By default the plugin resolves callees within the same file only. To enable cross-file resolution (e.g. this.service.method() via NestJS dependency injection), configure @typescript-eslint/parser with type information:

npm install --save-dev typescript-eslint
// eslint.config.js
import throws from '@mkvisuals/eslint-plugin-throws';
import tseslint from 'typescript-eslint';

export default [
  {
    files: ['**/*.{js,ts}'],
    plugins: { throws },
    rules: {
      'throws/no-uncaught-throws': ['warn', { requireThrowsAnnotation: true }],
    },
  },
  {
    files: ['**/*.ts'],
    languageOptions: {
      parser: tseslint.parser,
      parserOptions: {
        projectService: true,
        tsconfigRootDir: import.meta.dirname,
      },
    },
  },
];

When type information is available, the plugin uses TypeScript's type checker to resolve method calls across files and read @throws JSDoc tags from the target declaration. Without type information, it falls back to same-file resolution.

Rules

throws/no-uncaught-throws

Warns when a function annotated with @throws is called without a surrounding try/catch and the caller itself has no @throws annotation.

Examples

IncorrectfindItem declares @throws but the call is unguarded:

/** @throws {NotFoundException} */
function findItem(id) { /* ... */ }

function loadUser(id) {
  const item = findItem(id); // ⚠ warning
}

Correct — wrapped in try/catch:

function loadUser(id) {
  try {
    const item = findItem(id);
  } catch (error) {
    // handle it
  }
}

Correct — propagation via @throws on the caller:

/** @throws {NotFoundException} */
function loadUser(id) {
  const item = findItem(id); // OK — caller declares @throws
}

IDE suggestions (lightbulb fixes)

Each warning offers two quick-fix suggestions:

  1. Propagate @throws to enclosing function — adds/appends @throws {Type} to the caller's JSDoc
  2. Wrap in try/catch — wraps the statement in try { ... } catch (error) { throw error; }

Options

'throws/no-uncaught-throws': ['warn', {
  requireThrowsAnnotation: false,  // default
  removeUnnecessaryThrows: false,  // default
  fixStrategy: undefined,          // default (no autofix)
}]

| Option | Type | Default | Description | |--------|------|---------|-------------| | requireThrowsAnnotation | boolean | false | When true, warns when a function contains a throw statement but has no @throws JSDoc annotation. | | removeUnnecessaryThrows | boolean | false | When true, warns when a function has a @throws annotation but does not throw or call any function annotated with @throws. Autofixes by removing the stale annotation. | | fixStrategy | 'propagate' | 'try-catch' | — | Enables eslint --fix for uncaught throws. 'propagate' adds @throws to the calling function. 'try-catch' wraps the call in a try/catch block. When not set, only IDE suggestions are provided (no autofix). |

requireThrowsAnnotation

With requireThrowsAnnotation: true:

function riskyFn() {
  throw new NotFoundException(); // ⚠ missing @throws annotation
}

The suggestion Add @throws annotation will create or append the correct @throws {NotFoundException} tag.

removeUnnecessaryThrows

With removeUnnecessaryThrows: true:

/** @throws {NotFoundException} */
function safeFn() {
  // no throw, no calls to @throws functions
  return 42; // ⚠ unnecessary @throws annotation
}

The fix removes the @throws lines from the JSDoc. If the JSDoc only contained @throws, the entire comment is removed.

The force keyword

When calling external libraries that throw but don't have @throws annotations (compiled code, no JSDoc), you can mark a @throws tag with force to prevent it from being flagged as unnecessary:

/**
 * @throws {ORMException} force - TypeORM save() internally throws this
 */
async save(entity: User): Promise<void> {
  await this.repository.save(entity); // ✅ no "unnecessary" warning
}

The force keyword must be the first word after the type. Everything after it is treated as a normal description. Callers of this function will still need to handle or propagate the @throws as usual.

fixStrategy

With fixStrategy: 'propagate', running eslint --fix will automatically add @throws to the enclosing function:

/** @throws {NotFoundException} */
function findItem(id) { /* ... */ }

// Before fix:
function loadUser(id) {
  const item = findItem(id); // ⚠ warning
}

// After eslint --fix:
/** @throws {NotFoundException} */
function loadUser(id) {
  const item = findItem(id); // ✅ fixed
}

With fixStrategy: 'try-catch', running eslint --fix wraps the call instead:

// After eslint --fix:
function loadUser(id) {
  try {
    const item = findItem(id); // ✅ fixed
  } catch (error) {
    throw error;
  }
}

Resolution scope

The rule resolves callees within the same file using ESLint's scope analysis:

  • Direct calls: foo()
  • this.method() inside a class
  • obj.method() for locally-defined object literals

Cross-file resolution (TypeScript)

When @typescript-eslint/parser is configured with type information (parserOptions.projectService or parserOptions.project), the rule also resolves cross-file calls:

  • this.service.method() — injected dependencies (e.g. NestJS DI)
  • Any member expression call where TypeScript can resolve the type

The plugin reads @throws JSDoc tags from the resolved method declaration via TypeScript's type checker. No additional dependencies are required beyond typescript-eslint.

Type-level matching

The rule compares exception types between caller and callee. Only uncovered exception types produce warnings:

/** @throws {NotFoundException} */
function findItem(id: string) { /* ... */ }

/** @throws {NotFoundException} */
function loadUser(id: string) {
  return findItem(id); // ✅ NotFoundException is covered
}

If a callee throws multiple types, only the ones not declared in the caller's @throws are reported:

/** @throws {NotFoundException} */
/** @throws {ForbiddenException} */
function riskyCall() { /* ... */ }

/** @throws {NotFoundException} */
function handler() {
  riskyCall(); // ⚠ ForbiddenException is not covered
}

Exception inheritance (TypeScript)

When type information is available, the rule understands class inheritance. A parent exception type covers all of its subclasses:

class AppException extends Error {}
class NotFoundException extends AppException {}
class ForbiddenException extends AppException {}

/** @throws {NotFoundException} */
function findItem() { /* ... */ }

/** @throws {AppException} */
function handler() {
  findItem(); // ✅ NotFoundException extends AppException — covered
}

This works across files — the exception classes don't need to be imported in the calling file. The plugin resolves the inheritance chain via TypeScript's type checker.

Note: Exception inheritance checking requires TypeScript with type-aware linting (see cross-file setup). In plain JavaScript, only exact type name matching is used.

License

MIT