Taming the 'no-unused-vars' Noise in Callback-Heavy Code
Stop fighting your linter in callback-heavy projects. Learn how to configure ESLint's no-unused-vars rule to ignore API-mandated function parameters while still catching actual variable leaks.
24 Oct 2025, 23:54 UTC

The Conflict Between Clean Code and API Constraints
You are implementing a third-party library or a framework like Express or React. The API requires a specific function signature for a callback, but your current logic only needs the third argument. You leave the first two as placeholders, and suddenly your CI pipeline turns red with no-unused-vars errors.
The instinct is often to disable the rule entirely or prefix every unused variable with an underscore. However, disabling the rule creates a blind spot where genuine variable leaks—variables declared but never used—slip into production. The goal is to maintain a strict check on local variables while granting flexibility to function parameters dictated by external APIs.
Fine-Tuning the no-unused-vars Configuration
The no-unused-vars rule in ESLint is not a binary toggle; it is a configurable object. By separating how the linter treats variables (vars) versus arguments (args), you can eliminate noise without sacrificing safety.
The 'args' Strategy
The args option determines how the linter evaluates function parameters. You have three primary choices:
all: Every argument must be used. This is the strictest setting and often causes friction in callback-heavy code.after-used: Only arguments that come after the last used argument are flagged. This allows you to skip early parameters if you need a later one.none: Unused arguments are ignored entirely. This is the most pragmatic choice for projects that rely heavily on predefined API signatures.
Handling Destructuring and Errors
Modern JavaScript often uses rest elements to omit specific properties from an object. Setting ignoreRestSiblings: true allows you to destructure an object and collect the "rest" of the properties into a variable without being forced to use that variable.
Similarly, caughtErrors controls the catch(e) block. In many async/await patterns, you might want to catch an error to prevent a crash but not actually reference the error object e. Setting this to "none" prevents the linter from complaining about the unused error variable.
Practical Configuration Example
Below is a configuration for ESLint (v9.x) that ensures all local variables are tracked while ignoring the noise of unused function parameters and catch blocks.
// eslint.config.js
export default [
{
rules: {
'no-unused-vars': ['error', {
'vars': 'all', // Check all local/global variables
'args': 'none', // Ignore unused function arguments
'ignoreRestSiblings': true, // Allow unused rest elements
'caughtErrors': 'none' // Ignore unused catch block variables
}]
}
}
];
How this behaves in practice
Consider the following code snippet processed with the configuration above:
// 1. This will NOT trigger an error (args: 'none')
function handleRequest(req, res, next) {
res.send('Hello World');
// req and next are unused, but ignored
}
// 2. This WILL trigger an error (vars: 'all')
function calculateTotal(price) {
const taxRate = 0.05; // Error: 'taxRate' is assigned a value but never used
return price * 1.1;
}
// 3. This will NOT trigger an error (ignoreRestSiblings: true)
const user = { id: 1, name: 'Alice', email: '[contact removed]' };
const { email, ...userWithoutEmail } = user;
console.log(email);
// userWithoutEmail is unused, but allowed because it is a rest sibling
Trade-offs and Risks
The primary risk of setting args: 'none' is the loss of "typo detection." If you accidentally name a parameter userRequest but reference it as req inside the function body, ESLint will not warn you that userRequest is unused. You will instead get a no-undef error for req.
While no-undef usually catches this, the feedback loop is slightly different. In very large teams, args: 'after-used' is often a safer middle ground, as it forces developers to be intentional about which parameters they are skipping.
Verification and Implementation
To verify this configuration, run the ESLint CLI from your project root with the appropriate permissions to read your config file:
npx eslint .
Expected Result: The command should pass for functions with unused arguments but fail for any variable declared within a function body that is not referenced. If the linter still flags unused arguments, ensure your eslint.config.js is not being overridden by a more specific configuration file in a sub-directory.
Rollback: To return to default strict behavior, remove the options object from the no-unused-vars rule or set args back to 'all'.
0 replies
A thoughtful contribution can make all the difference. Be the first to share one.