# Why filter(Boolean) Is Not a Nullish Filter in TypeScript 🔧

Hoi hoi! 👋

I'm [@nyaomaru](https://github.com/nyaomaru), a frontend engineer building small TypeScript OSS with shoyu ramen. 😸🍜

Today, let's look at a very small line of JavaScript.

```ts
values.filter(Boolean);
```

You've probably seen it before.

*   It's short.
    
*   It's convenient.
    
*   And sometimes, it's exactly what you want.
    

But if your intention is

> Remove only `null` and `undefined`

then `filter(Boolean)` is doing more than you asked.

Let's see why! 👀

![Image description](https://dev-to-uploads.s3.us-east-2.amazonaws.com/uploads/articles/kduv6f80h33vcf2ry5sr.png align="center")

* * *

## 🕳️ The Small Trap in `filter(Boolean)`

Imagine we have this array.

```ts
const values = ["ready", "", 0, false, null, undefined];
```

Suppose we try to remove the missing values like this.

```ts
const result = values.filter(Boolean);
```

At runtime, what remains?

```ts
["ready"];
```

Wait.

We only wanted to remove

```ts
null;
undefined;
```

But we also lost

```ts
"";
0;
false;
```

Why?

Because `Boolean` converts its argument to a boolean and keeps only values whose result is `true`.

It isn't checking whether a value is nullish.

* * *

## 🤔 Falsy and Nullish Are Different

JavaScript considers all of these values falsy

```ts
false;
0;
("");
null;
undefined;
NaN;
```

But these two requirements are different

> Remove every falsy value

and

> Remove null and undefined

In real applications, `0`, `false`, and `''` can all be completely valid data.

For example

```ts
type Settings = {
  retryCount: number;
  notificationsEnabled: boolean;
  nickname: string;
};
```

These can all be valid

```ts
retryCount = 0;
notificationsEnabled = false;
nickname = "";
```

Falsy does not mean missing.

* * *

## 🧠 There Is a TypeScript Difference Too

`filter(Boolean)` removes falsy values at runtime, but `Boolean` is not a type guard.

TypeScript therefore does not generally know that `null` and `undefined` are gone.

```ts
const values: Array<string | number | boolean | null | undefined> = [
  "ready",
  "",
  0,
  false,
  null,
  undefined,
];

const result = values.filter(Boolean);

// result: Array<string | number | boolean | null | undefined>
```

So `filter(Boolean)` can be right when truthiness is the runtime rule, but it doesn't express a nullish-removal rule to either JavaScript readers or the TypeScript type system. 🙀

* * *

## ✅ Say What You Actually Mean

If the rule is

> Keep everything except `null` and `undefined`

we can write exactly that

```ts
const values: Array<string | null | undefined> = [
  "Ada",
  null,
  "Linus",
  undefined,
];

const names = values.filter(
  (value): value is string => value !== null && value !== undefined,
);
```

Now

```ts
// names: string[]
```

This is perfectly good TypeScript.

You don't need a library for a one-off check.

### ⚠️ A browser edge case: avoid `value != null`

You may also see this shorter version

```ts
values.filter((value): value is string => value != null);
```

For ordinary values, it looks equivalent to checking both `null` and `undefined`. In browsers, though, `document.all` is a historical compatibility exception.

```ts
document.all == null; // true
document.all != null; // false

document.all === null; // false
document.all === undefined; // false
```

`document.all` is an object, not `null` or `undefined`, but loose equality　treats it as nullish. So if the contract is *only* “remove `null` and　`undefined`”, use strict comparisons.

```ts
(value): value is string => value !== null && value !== undefined;
```

You will rarely encounter this in application code, but the edge case is why a precise nullish guard should not be implemented with `!= null`.

> See MDN's [equality operator reference](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Equality) for the compatibility rule behind this behavior.

* * *

## 🔁 What If You Keep Writing It?

The interesting part starts when the same meaning appears repeatedly.

```ts
(value): value is string => value !== null && value !== undefined;
```

Maybe in

*   API adapters
    
*   selectors
    
*   UI helpers
    
*   mapped data
    
*   utility functions
    

At that point, the useful abstraction isn't really the syntax.

It's the meaning

> This value is not nullish.

That's where I like using a named type guard.

With `is-kit`

```ts
import { isNotNil } from "is-kit";

const names = values.filter(isNotNil);
```

The result still narrows naturally

```ts
// names: string[]
```

And unlike `Boolean`, valid falsy values stay intact.

```ts
const values: Array<string | number | boolean | null | undefined> = [
  "ready",
  "",
  0,
  false,
  null,
  undefined,
];

const result = values.filter(isNotNil);

// ['ready', '', 0, false]
// result: Array<string | number | boolean>
```

* * *

## 🧩 A Practical Example

Nullable values often appear after `map`.

```ts
type User = {
  id: string;
  nickname?: string | null;
};

const users: User[] = [
  { id: "1", nickname: "nyaomaru" },
  { id: "2", nickname: null },
  { id: "3" },
];
```

We want only the existing nicknames.

```ts
const nicknames = users.map((user) => user.nickname).filter(isNotNil);

// string[]
```

This is the kind of place where a reusable guard feels natural to me.

The array transformation stays ordinary JavaScript, while TypeScript knows that the nullish values are gone.

* * *

## ⚖️ Which One Should You Use?

I think the choice is pretty simple.

Use

```ts
filter(Boolean);
```

when you genuinely want

> Keep only truthy values.

Use an inline predicate when the nullish check appears once:

```ts
values.filter(
  (value): value is string => value !== null && value !== undefined,
);
```

And use a reusable guard such as

```ts
values.filter(isNotNil);
```

when that same meaning appears repeatedly.

There is no need to turn every condition into an abstraction.

* * *

## 🔍 Can ESLint Catch This?

I recently released [`eslint-plugin-is-kit`](https://github.com/nyaomaru/eslint-plugin-is-kit), a type-aware ESLint plugin for TypeScript predicates.

One of its rules looks specifically for ambiguous `filter(Boolean)` calls.

```ts
declare const values: Array<string | null>;

values.filter(Boolean);
```

Because the element type contains both `null` and a non-nullish falsy value (`""`), the plugin can warn that `Boolean` may remove more than just the missing value.

But it does **not** simply ban `filter(Boolean)`.

```ts
declare const values: number[];

values.filter(Boolean);
```

There is no `null` or `undefined` in the element type, so there is no evidence that nullish removal was intended.

The rule is deliberately conservative.

The initial `v0.1.0` release includes four type-aware rules for:

*   ambiguous `filter(Boolean)` calls
    
*   redundant `is-kit` predicates
    
*   repeated inline nullish filters that could use `isNotNil`
    
*   inline predicates that can be expressed with reusable type guards
    

For example

```ts
values.filter((value) => typeof value === "string");
```

can be expressed as

```ts
values.filter(isString);
```

when doing so preserves the runtime behavior and useful TypeScript narrowing.

If this kind of check would be useful in your codebase:

*   GitHub: https://github.com/nyaomaru/eslint-plugin-is-kit
    
*   npm: https://www.npmjs.com/package/eslint-plugin-is-kit
    

It's still an early release, so false positives and ideas for useful rules are very welcome. 😸

* * *

## 🎯 The Important Part

The main point isn't really `isNotNil`.

It's this

> **Falsy values and missing values are not the same thing.**

`filter(Boolean)` is not bad code.

It just expresses a different requirement.

So before writing

```ts
values.filter(Boolean);
```

ask

> Do I want to remove falsy values, or only nullish values?

That tiny distinction can prevent valid data like `0`, `false`, and `''` from disappearing unexpectedly. 😸

I also wrote a more complete guide about nullish filtering on the [is-kit documentation site](https://is-kit.dev/guides/filter-null-and-undefined), including `isNil`, `isNotNil`, and the different approaches.

If you like small reusable TypeScript type guards, `is-kit` is open source too! And don't forget to put a star! ⭐

https://github.com/nyaomaru/is-kit

Thanks for reading! 🙌
