JSPM

eslint-plugin-no-truthy-collections

1.0.0
  • ESM via JSPM
  • ES Module Entrypoint
  • Export Map
  • Keywords
  • License
  • Repository URL
  • TypeScript Types
  • README
  • Created
  • Published
  • Downloads 20
  • Score
    100M100P100Q73487F
  • License MIT

ESLint plugin that prevents dangerous boolean coercion of arrays and objects in JavaScript and TypeScript

Package Exports

  • eslint-plugin-no-truthy-collections
  • eslint-plugin-no-truthy-collections/lib/index.js

This package does not declare an exports field, so the exports above have been automatically detected and optimized by JSPM instead. If any package subpath is missing, it is recommended to post an issue to the original package (eslint-plugin-no-truthy-collections) to support the "exports" field. If that is not possible, create a JSPM override to customize the exports field for this package.

Readme

eslint-plugin-no-truthy-collections

🚀 ESLint plugin that prevents dangerous boolean coercion of arrays and objects in JavaScript and TypeScript

Rules

no-truthy-collections

Prevents dangerous boolean coercion of arrays and objects in JavaScript and TypeScript. Arrays and objects are always truthy in JS, even when empty - this catches common bugs from developers expecting Python-like falsy behavior.

❌ Problematic Code

// Arrays are always truthy, even when empty!
if (items) {
  // 🐛 Always true, even for []
  render(items);
}

// Objects are always truthy, even when empty!
if (config) {
  // 🐛 Always true, even for {}
  applyConfig(config);
}

// Array-like objects too!
if (new Set()) {
  // 🐛 Always true, even when empty
  processSet();
}

✅ Correct Code

// Check for actual content
if (items.length > 0) {
  render(items);
}

// Check for actual properties
if (Object.keys(config).length > 0) {
  applyConfig(config);
}

// Check for actual size
if (mySet.size > 0) {
  processSet();
}

TypeScript Support ⚡

Enhanced detection when using TypeScript with type information:

// Standard detection (works in JS too)
if ([]) { }                    // 🚨 Always flagged

// Enhanced TypeScript detection via type information
function processItems(items: string[]) {
  if (items) { }               // 🚨 Detected via TypeScript types
    return items.map(x => x.toUpperCase());
  }
}

// Interface types
interface Config {
  settings: Record<string, any>;
}
const config: Config = { settings: {} };
if (config.settings) { }      // 🚨 Detected via TypeScript types

// Generic constraints
function check<T extends any[]>(arr: T) {
  if (arr) { }                 // 🚨 Enhanced detection with generics
    return arr.length;
  }
}

// Union types (smart handling)
let items: string[] | null = [];
if (items) { }                 // ✅ Valid null check (not flagged)

Installation

npm install --save-dev eslint-plugin-no-truthy-collections
# or
bun add --dev eslint-plugin-no-truthy-collections

Usage

JavaScript Projects

// eslint.config.js
import noTruthyCollections from 'eslint-plugin-no-truthy-collections';

export default [
  {
    plugins: {
      'no-truthy-collections': noTruthyCollections,
    },
    rules: {
      'no-truthy-collections/no-truthy-collections': 'error',
    },
  },
];
// eslint.config.js
import noTruthyCollections from 'eslint-plugin-no-truthy-collections';
import tsParser from '@typescript-eslint/parser';

export default [
  {
    files: ['**/*.{js,ts,tsx}'],
    languageOptions: {
      parser: tsParser,
      parserOptions: {
        project: './tsconfig.json', // Enables enhanced type detection
      },
    },
    plugins: {
      'no-truthy-collections': noTruthyCollections,
    },
    rules: {
      'no-truthy-collections/no-truthy-collections': 'error',
    },
  },
];

💡 See eslint.config.typescript.example.js for a complete TypeScript setup example

Using Preset Configurations

// eslint.config.js
import noTruthyCollections from 'eslint-plugin-no-truthy-collections';

export default [
  // Recommended preset
  {
    plugins: { 'no-truthy-collections': noTruthyCollections },
    ...noTruthyCollections.configs.recommended,
  },

  // Or strict preset (includes naming checks)
  {
    plugins: { 'no-truthy-collections': noTruthyCollections },
    ...noTruthyCollections.configs.strict,
  },
];

Configuration Options

{
  'no-truthy-collections/no-truthy-collections': ['error', {
    checkArrays: true,           // Check array literals and constructors
    checkObjects: true,          // Check object literals and constructors
    checkArrayLike: true,        // Check Set, Map, etc.
    strictNaming: false,         // Check variable names for collection hints
    allowExplicitBoolean: true,  // Allow Boolean() and !! coercion
  }]
}

Option Details

Option Default Description
checkArrays true Detect arrays in boolean contexts
checkObjects true Detect objects in boolean contexts
checkArrayLike true Detect Set, Map, etc. in boolean contexts
strictNaming false Use variable names to detect collections
allowExplicitBoolean true Allow Boolean(array) and !!array

Examples

Basic Detection

// ❌ Will be flagged
if ([]) {
} // Always true
if ({}) {
} // Always true
if (new Set()) {
} // Always true
arr.filter(x => x) && process(); // Always true

// ✅ Auto-fixed to
if ([].length > 0) {
}
if (Object.keys({}).length > 0) {
}
if (new Set().size > 0) {
}
arr.filter(x => x).length > 0 && process();

TypeScript Enhanced Detection

// ❌ Enhanced detection with TypeScript
function processUsers(users: User[]) {
  if (users) {
    // 🚨 Detected via type information
    return users.filter(u => u.active);
  }
}

type Config = Record<string, any>;
const config: Config = {};
if (config) {
  // 🚨 Detected via type alias
  loadConfig(config);
}

// ✅ Proper TypeScript patterns
function processUsers(users: User[]) {
  if (users.length > 0) {
    // ✅ Explicit length check
    return users.filter(u => u.active);
  }
}

function loadUserConfig(config: Config | null) {
  if (config && Object.keys(config).length > 0) {
    // ✅ Null check + size check
    loadConfig(config);
  }
}

Smart Suggestions

// ❌ Suspicious pattern detected
if (new Set([item])) {
  // Always has size 1!
  process();
}

// 💡 Helpful suggestions:
// 1. Check the element: if (item)
// 2. Check from element: new Set(item).size > 0
// 3. Check size: new Set([item]).size > 0

Strict Naming Mode

// With strictNaming: true

// ❌ Will be flagged based on variable names
if (itemsArray) {
} // Naming suggests array
if (configObject) {
} // Naming suggests object
if (userList) {
} // Naming suggests array

// ✅ Better patterns
if (itemsArray.length > 0) {
}
if (Object.keys(configObject).length > 0) {
}
if (userList.length > 0) {
}

Why This Rule Matters

JavaScript's truthy behavior with collections causes subtle bugs:

function processItems(items = []) {
  if (items) {
    // 🐛 BUG: Always true!
    return items.map(x => x * 2);
  }
  return [];
}

// This fails silently:
processItems([]); // Returns [] but we expected it to work

TypeScript doesn't prevent this either:

function processItems(items: number[] = []) {
  if (items) {
    // 🐛 BUG: TypeScript allows this!
    return items.map(x => x * 2);
  }
  return [];
}

This rule catches these issues early and suggests proper fixes.

Rule Details

  • Type: Problem (catches bugs)
  • Fixable: Yes (auto-fixes available)
  • Suggestions: Yes (multiple fix options)
  • TypeScript: Fully supported with enhanced detection
  • Performance: Optimized with file skipping for build directories

Requirements

  • ESLint: 8.0.0 or higher
  • Node.js: 16.0.0 or higher
  • TypeScript (optional): 4.0.0 or higher for enhanced detection
  • @typescript-eslint/parser (optional): 6.0.0 or higher for type information

License

MIT

Contributing

Issues and PRs welcome! Make sure tests pass and code is formatted:

# Run tests
bun test

# Format code
bun run format

# Lint code
bun run lint

# Format + lint in one command
bun run format:lint

# Full check (runs in CI)
bun run prepack