Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion packages/comparisons/src/data.json
Original file line number Diff line number Diff line change
Expand Up @@ -16573,7 +16573,8 @@
"flint": {
"name": "strictBooleanExpressions",
"plugin": "ts",
"preset": "logical"
"preset": "logical",
"status": "implemented"
}
},
{
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,86 @@
---
description: "Disallow non-boolean types in boolean contexts that may cause unexpected behavior."
title: "strictBooleanExpressions"
topic: "rules"
---

import { Tabs, TabItem } from "@astrojs/starlight/components";
import { RuleEquivalents } from "~/components/RuleEquivalents";
import RuleSummary from "~/components/RuleSummary.astro";

<RuleSummary plugin="ts" rule="strictBooleanExpressions" />

JavaScript allows any value in boolean contexts like `if` statements, ternary expressions, and logical negation.
While primitives like strings and numbers have well-understood coercion rules, certain types in boolean contexts can indicate bugs or unclear code.

This rule reports:

- Using `any` in a boolean context, which defeats type safety
- Nullable booleans (`boolean | null` or `boolean | undefined`), where `null`/`undefined` silently coerce to `false`
- Non-nullable objects that are always truthy, making the condition redundant

## Examples

<Tabs>
<TabItem label="❌ Incorrect">

```ts
declare const value: any;
if (value) {
}

declare const flag: boolean | null;
if (flag) {
}

const config = { enabled: true };
if (config) {
}
```

</TabItem>
<TabItem label="✅ Correct">

```ts
declare const flag: boolean;
if (flag) {
}

declare const text: string;
if (text) {
}

declare const count: number;
if (count) {
}

declare const item: object | null;
if (item) {
}

declare const flag: boolean | null;
if (flag === true) {
}
if (flag ?? false) {
}
```

</TabItem>
</Tabs>

## Options

This rule is not configurable.

## When Not To Use It

If your codebase intentionally relies on truthy/falsy coercion for nullable booleans or uses `any` types extensively, you may want to disable this rule.

## Further Reading

- [TypeScript Handbook: Narrowing](https://www.typescriptlang.org/docs/handbook/2/narrowing.html)
- [MDN: Falsy values](https://developer.mozilla.org/en-US/docs/Glossary/Falsy)

## Equivalents in Other Linters

<RuleEquivalents pluginId="ts" ruleId="strictBooleanExpressions" />
2 changes: 2 additions & 0 deletions packages/ts/src/plugin.ts
Original file line number Diff line number Diff line change
Expand Up @@ -222,6 +222,7 @@ import selfComparisons from "./rules/selfComparisons.ts";
import sequences from "./rules/sequences.ts";
import shadowedRestrictedNames from "./rules/shadowedRestrictedNames.ts";
import sparseArrays from "./rules/sparseArrays.ts";
import strictBooleanExpressions from "./rules/strictBooleanExpressions.ts";
import symbolDescriptions from "./rules/symbolDescriptions.ts";
import typeofComparisons from "./rules/typeofComparisons.ts";
import unassignedVariables from "./rules/unassignedVariables.ts";
Expand Down Expand Up @@ -464,6 +465,7 @@ export const ts = createPlugin({
sequences,
shadowedRestrictedNames,
sparseArrays,
strictBooleanExpressions,
symbolDescriptions,
typeofComparisons,
unassignedVariables,
Expand Down
170 changes: 170 additions & 0 deletions packages/ts/src/rules/strictBooleanExpressions.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,170 @@
import { ruleTester } from "./ruleTester.ts";
import rule from "./strictBooleanExpressions.ts";

ruleTester.describe(rule, {
invalid: [
{
code: `
declare const value: any;
if (value) {}
`,
snapshot: `
declare const value: any;
if (value) {}
~~~~~
Using \`any\` in a boolean context can cause unexpected behavior.
`,
},
{
code: `
declare const value: any;
while (value) {}
`,
snapshot: `
declare const value: any;
while (value) {}
~~~~~
Using \`any\` in a boolean context can cause unexpected behavior.
`,
},
{
code: `
declare const value: any;
do {} while (value);
`,
snapshot: `
declare const value: any;
do {} while (value);
~~~~~
Using \`any\` in a boolean context can cause unexpected behavior.
`,
},
{
code: `
declare const value: any;
for (; value; ) {}
`,
snapshot: `
declare const value: any;
for (; value; ) {}
~~~~~
Using \`any\` in a boolean context can cause unexpected behavior.
`,
},
{
code: `
declare const value: any;
const result = value ? 1 : 0;
`,
snapshot: `
declare const value: any;
const result = value ? 1 : 0;
~~~~~
Using \`any\` in a boolean context can cause unexpected behavior.
`,
},
{
code: `
declare const value: any;
const negated = !value;
`,
snapshot: `
declare const value: any;
const negated = !value;
~~~~~
Using \`any\` in a boolean context can cause unexpected behavior.
`,
},
{
code: `
declare const flag: boolean | null;
if (flag) {}
`,
snapshot: `
declare const flag: boolean | null;
if (flag) {}
~~~~
Nullable booleans require explicit null checks in conditions.
`,
},
{
code: `
declare const flag: boolean | undefined;
if (flag) {}
`,
snapshot: `
declare const flag: boolean | undefined;
if (flag) {}
~~~~
Nullable booleans require explicit null checks in conditions.
`,
},
{
code: `
declare const flag: boolean | null | undefined;
if (flag) {}
`,
snapshot: `
declare const flag: boolean | null | undefined;
if (flag) {}
~~~~
Nullable booleans require explicit null checks in conditions.
`,
},
{
code: `
const config = { enabled: true };
if (config) {}
`,
snapshot: `
const config = { enabled: true };
if (config) {}
~~~~~~
This condition is always truthy.
`,
},
{
code: `
function getHandler() { return () => {}; }
if (getHandler()) {}
`,
snapshot: `
function getHandler() { return () => {}; }
if (getHandler()) {}
~~~~~~~~~~~~
This condition is always truthy.
`,
},
{
code: `
class Service {}
const service = new Service();
if (service) {}
`,
snapshot: `
class Service {}
const service = new Service();
if (service) {}
~~~~~~~
This condition is always truthy.
`,
},
],
valid: [
`const flag = true; if (flag) {}`,
`const flag = false; if (flag) {}`,
`declare const flag: boolean; if (flag) {}`,
`const text = "hello"; if (text) {}`,
`declare const text: string; if (text) {}`,
`const count = 42; if (count) {}`,
`declare const count: number; if (count) {}`,
`declare const item: object | null; if (item) {}`,
`declare const item: object | undefined; if (item) {}`,
`declare const nullable: string | null; if (nullable != null) {}`,
`declare const flag: boolean | null; if (flag === true) {}`,
`declare const flag: boolean | undefined; if (flag ?? false) {}`,
`const items = [1, 2, 3]; if (items.length) {}`,
`declare const items: number[]; if (items.length) {}`,
`declare const getter: (() => void) | undefined; if (getter) {}`,
],
});
Loading