This TypeScript error occurs when you declare a variable, function, or class property but never reference it in your code. It's triggered by the noUnusedLocals compiler option to help maintain clean code by removing dead code and unused declarations.
The "Property is declared but never used" error is a TypeScript compiler warning that appears when you have the `noUnusedLocals` option enabled in your tsconfig.json. This feature helps identify code that is declared but never actually used, which can indicate: 1. **Dead code**: Variables or functions that were written but never called 2. **Unused imports**: Import statements that bring in modules you don't use 3. **Leftover code**: Code from previous implementations that wasn't cleaned up 4. **Unused parameters**: Function parameters that are declared but not referenced This error doesn't prevent compilation but serves as a warning to keep your codebase clean and maintainable. It's part of TypeScript's strict mode family of checks designed to improve code quality.
First, determine if the unused declaration should be kept or removed:
// Example of unused variable
const unusedVariable = "hello"; // Warning: 'unusedVariable' is declared but never used
// Ask yourself:
// 1. Is this variable part of a future feature?
// 2. Is it used in a different file (exported)?
// 3. Is it a placeholder for upcoming implementation?
// If genuinely unused, remove it:
// const unusedVariable = "hello"; // DELETE THIS LINE
// If it will be used later, you can prefix with underscore to suppress warning:
const _unusedVariable = "hello"; // No warning with underscore prefixFor functions and classes:
// Unused function
function calculateTotal() { // Warning
return 100;
}
// If needed for exports or future use:
export function calculateTotal() { // No warning if exported
return 100;
}
// Or prefix with underscore:
function _calculateTotal() { // No warning
return 100;
}Unused imports are a common cause of this warning:
// WRONG - imported but not used
import React, { useState, useEffect } from 'react'; // Warning if useState/useEffect not used
// CORRECT - only import what you need
import React from 'react';
// If you need them later:
import React from 'react';
// import { useState, useEffect } from 'react'; // Comment out until needed
// Using a linter or IDE can help:
// In VS Code: Right-click → "Organize Imports" (Ctrl+Shift+O)
// This automatically removes unused imports
// For type-only imports that shouldn't trigger warnings:
import type { User } from './types'; // Type-only imports don't count as unusedMost modern IDEs can automatically remove unused imports. In VS Code:
1. Open the command palette (Ctrl+Shift+P)
2. Type "Organize Imports"
3. Select the command to clean up imports
For function parameters that must exist in the signature but aren't used:
// WRONG - unused parameter triggers warning
function handleClick(event: MouseEvent) {
console.log('Clicked!');
// 'event' parameter not used - warning
}
// CORRECT - prefix with underscore
function handleClick(_event: MouseEvent) {
console.log('Clicked!');
// No warning with underscore prefix
}
// For multiple unused parameters:
function fetchData(_url: string, _options: RequestInit, callback: (data: any) => void) {
// Only callback is used, url and options are intentionally unused
callback({});
}
// For destructuring with unused properties:
const { usedProp, _unusedProp } = someObject; // Prefix unused destructured properties
// In try-catch where error might not be used:
try {
riskyOperation();
} catch (_error) { // Prefix if error not used
console.log('Operation failed');
}The underscore convention tells TypeScript (and other developers) that the parameter is intentionally unused.
Control the strictness of this check in your TypeScript configuration:
// tsconfig.json
{
"compilerOptions": {
// Strict mode options (all enabled by default in strict mode)
"noUnusedLocals": true, // Check for unused local variables
"noUnusedParameters": true, // Check for unused function parameters
"noImplicitReturns": true, // All code paths must return a value
// Alternative: Use strict mode which includes these
"strict": true // Enables all strict type-checking options
}
}To disable the warning (not recommended for production code):
{
"compilerOptions": {
"noUnusedLocals": false, // Disable unused locals check
"noUnusedParameters": false // Disable unused parameters check
}
}For specific files only (using comments):
// @ts-nocheck - Disable all checks for this file
// or
// @ts-ignore - Disable next line
const unused = "test"; // @ts-ignoreHowever, disabling these checks reduces code quality benefits.
ESLint provides more granular control over unused code detection:
# Install ESLint with TypeScript support
npm install --save-dev eslint @typescript-eslint/parser @typescript-eslint/eslint-pluginCreate .eslintrc.js:
module.exports = {
parser: '@typescript-eslint/parser',
plugins: ['@typescript-eslint'],
extends: [
'eslint:recommended',
'plugin:@typescript-eslint/recommended'
],
rules: {
'@typescript-eslint/no-unused-vars': ['warn', {
'argsIgnorePattern': '^_',
'varsIgnorePattern': '^_',
'caughtErrorsIgnorePattern': '^_'
}],
'no-unused-vars': 'off' // Turn off base rule in favor of TypeScript rule
}
};ESLint advantages over TypeScript's noUnusedLocals:
- More configurable ignore patterns
- Can be warnings vs errors
- Better editor integration
- Fixable with --fix flag
Run ESLint to find and fix unused code:
npx eslint src/ --fixSome patterns look like unused code but are actually used:
// 1. Destructuring with rest operator
const { used, ...rest } = obj; // 'rest' might appear unused but is valid
// 2. Functions used as types
type Handler = (event: Event) => void;
const onClick: Handler = (event) => { // 'event' appears unused but is part of type
console.log('clicked');
};
// 3. Module augmentation
declare global {
interface Window {
myCustomProp: string; // Augmentation, not direct usage
}
}
// 4. Side-effect imports
import './styles.css'; // Import for side effects, no named imports
import 'polyfill'; // Polyfill that modifies global scope
// 5. Re-export patterns
export { Button } from './Button'; // Re-export, Button not "used" in this file
// 6. Generic type parameters
function identity<T>(value: T): T { // T might appear unused in simple cases
return value;
}
// 7. Decorator parameters
function Log(target: any, key: string) { // Parameters used by decorator system
// target and key might not be referenced in body
}
// For these cases, use ignore comments or underscore prefix:
import './styles.css'; // OK - side effect import
const { used, ..._rest } = obj; // Prefix if rest truly unused### noUnusedLocals vs noUnusedParameters
TypeScript has two related options:
- noUnusedLocals: Checks for unused local variables, functions, classes, imports, etc.
- noUnusedParameters: Specifically checks for unused function parameters
Both are included when strict: true is set.
### Performance Implications
The unused code checks have minimal performance impact during compilation. However, they can significantly improve:
- Bundle size: Removing unused code reduces final bundle size
- Maintainability: Cleaner code is easier to understand and modify
- Runtime performance: Less code to parse and execute
### Integration with Build Tools
Webpack: Use TerserPlugin for dead code elimination in production:
const TerserPlugin = require('terser-webpack-plugin');
module.exports = {
optimization: {
minimize: true,
minimizer: [new TerserPlugin()],
},
};Vite: Uses esbuild which performs tree-shaking automatically.
Rollup: Has built-in tree-shaking; unused exports are removed.
### Type-Only Imports
TypeScript 3.8+ introduced type-only imports that don't count as "used" for noUnusedLocals:
import type { User, Product } from './types'; // Type-only, no runtime import
import { createUser } from './api'; // Value import
// Mixed import (TypeScript 4.5+):
import { type User, createUser } from './api';### Migration Strategy
When enabling noUnusedLocals on an existing codebase:
1. Enable as warning first: "noUnusedLocals": true
2. Use // @ts-ignore comments temporarily
3. Gradually fix warnings file by file
4. Consider using ESLint's --fix option for bulk fixes
5. Once clean, consider making it an error: "noUnusedLocals": "error" in ESLint
### False Positives in Test Files
Test files often have setup code that appears unused:
// In Jest/Vitest test files
describe('MyComponent', () => {
it('should render', () => {
// Test code
});
});
// The describe and it functions might trigger unused warnings
// Solution: Configure ESLint/TypeScript to ignore test filesCreate a separate tsconfig for tests:
// tsconfig.test.json
{
"extends": "./tsconfig.json",
"compilerOptions": {
"noUnusedLocals": false,
"noUnusedParameters": false
}
}Type parameter 'X' is not used in the function signature
How to fix "Type parameter not used in function signature" in TypeScript
Type parameter 'X' is defined but never used
How to fix "Type parameter is defined but never used" in TypeScript
Function expression requires a return type
Function expression requires a return type
Value of type 'string | undefined' is not iterable
How to fix "Value is not iterable" in TypeScript
Type 'undefined' is not assignable to type 'string'
How to fix "Type undefined is not assignable to type string" in TypeScript