Typescript Check If Array Is Empty

8 min read

Checking if an array is empty is one of the most fundamental operations in TypeScript development, yet it is surprisingly easy to get wrong if you rely solely on JavaScript's loose typing habits. Because of that, because arrays in JavaScript are objects, they are inherently truthy even when they contain zero elements. This behavior trips up developers migrating from strictly typed languages or those new to the ecosystem. Mastering the idiomatic, type-safe ways to perform this check ensures your code is strong, readable, and free from runtime surprises Surprisingly effective..

Why Standard Truthy Checks Fail in TypeScript

In vanilla JavaScript, a common pattern to check for content is if (myArray). Even so, an empty array [] evaluates to true in a boolean context. This is because [] is an object reference, and all objects are truthy.

const emptyArray: number[] = [];

if (emptyArray) {
  console.log("This runs, but the array is empty!"); // Logic error
}

TypeScript’s type system does not automatically narrow the type of emptyArray inside that block to "non-empty array." It remains number[]. To write safe, expressive code, you must use explicit checks that communicate intent to both the compiler and future maintainers.

The Gold Standard: Checking the length Property

The most universal, performant, and type-safe method is checking the length property. Since length is a number, comparing it against 0 provides a clear boolean result.

Explicit Comparison (Recommended)

function processUsers(users: User[]) {
  if (users.length === 0) {
    console.log("No users found.");
    return;
  }
  
  // TypeScript knows users has at least one element here
  users.forEach(user => console.log(user.name));
}

Why this wins:

  1. Clarity: length === 0 reads like English.
  2. Performance: Accessing a property is O(1); no iteration occurs.
  3. Type Narrowing: While TypeScript doesn't natively narrow T[] to [T, ...T[]] (a non-empty tuple) based solely on this check in older versions, modern control flow analysis handles the "empty vs not empty" logic flow perfectly for control structures like return or throw.

Implicit Coercion (Common but Riskier)

You will often see if (!Which means users. length). This works because 0 is falsy The details matter here..

if (!users.length) { /* handle empty */ }

While concise, this relies on implicit type coercion. Many style guides (like Airbnb or Google) and linters (ESLint no-implicit-coercion) prefer the explicit === 0 comparison to prevent bugs where a variable might unexpectedly be null or undefined instead of an array Worth keeping that in mind..

This changes depending on context. Keep that in mind.

Handling null and undefined: The Safety Net

In real-world applications, API responses or optional props often result in types like User[] | null or User[] | undefined. Accessing .Also, length on null throws a runtime TypeError. You must guard against this before checking length.

Optional Chaining (Modern Standard)

TypeScript 3.7+ introduced optional chaining (?.), making this clean and safe.

const fetchData = (): Promise => /* ... */;

async function render() {
  const users = await fetchData();
  
  // If users is null/undefined, users?.Day to day, length returns undefined. // undefined === 0 is false, so the block runs correctly for "empty or missing".
  if (users?.

### The "Falsy Catch-All" Pattern

If you want to treat `null`, `undefined`, and `[]` identically (often the case for UI rendering), a simple falsy check on the variable itself works *only if you know it's an array or nullish*.

```typescript
// Type: User[] | null | undefined
if (!users) { 
  // Handles null, undefined, AND [] because [] is truthy? 
  // WAIT: [] is truthy! 
  // This block runs for null/undefined but NOT for [].
}

Critical Correction: Because [] is truthy, if (!users) does not catch empty arrays. It only catches null/undefined Less friction, more output..

To catch all three (null, undefined, empty), combine the checks:

// solid check for "Nothing to show"
if (!users || users.length === 0) {
  renderEmptyState();
}

Or, using optional chaining for a one-liner:

if (!users?.length) {
  // Runs if users is null, undefined, OR empty array []
  renderEmptyState();
}

How it works:

  • users is null/undefined -> users?.length is undefined -> !undefined is true.
  • users is [] -> users?.length is 0 -> !0 is true.
  • users is [1, 2] -> users?.length is 2 -> !2 is false.

This is arguably the most elegant "check if array is empty or missing" pattern in modern TypeScript.

Advanced: Type Guards for "Non-Empty Array" Types

For library authors or complex domain logic, you often want the type system to guarantee an array has items after a check. Consider this: you can create a User-Defined Type Guard to narrow T[] to NonEmptyArray<T> (often defined as [T, ... T[]]).

Defining the Types

// A tuple type representing an array with at least one element
type NonEmptyArray = [T, ...T[]];

Writing the Type Guard

function isNonEmptyArray(arr: T[]): arr is NonEmptyArray {
  return arr.length > 0;
}

Usage and Automatic Narrowing

function getFirstItem(items: T[]): T | undefined {
  if (isNonEmptyArray(items)) {
    // Inside this block, 'items' is typed as NonEmptyArray
    // TypeScript KNOWS items[0] exists. No undefined check needed.
    return items[0]; 
  }
  return undefined;
}

This pattern shifts the burden of proof from the consumer (who might forget to check) to the utility function, making your APIs significantly safer It's one of those things that adds up. But it adds up..

Performance Considerations: length vs .shift() vs .pop()

A common anti-pattern born from other languages is modifying the array to check it.

❌ Bad: Destructive Checks

// Destroys data! Do not do this.
if (myArray.shift() === undefined) { ... } 
if (myArray.pop() === undefined) { ... }

These are O(1) for pop but O(n) for shift (re-indexing), and they mutate state. Never mutate data just to inspect it.

✅ Good: Read-Only Access

myArray.length is a simple property lookup on the array object. It is O(1), allocates no memory, and causes no side effects. It is the absolute fastest way to determine emptiness.

Framework Specifics: React, Vue, and Angular

While the core TypeScript logic remains identical, framework rendering patterns influence where and how you write the check.

React (JSX Conditional Rendering)

In

React (JSX Conditional Rendering)

In React, the check typically lives directly inside the JSX or immediately before the return statement. Because JSX treats false, null, undefined, and true as "holes" (rendering nothing), the && operator is the idiomatic standard.

interface User { id: number; name: string; }

function UserList({ users }: { users: User[] | null }) {
  // 1. Early return pattern (Best for complex empty states)
  if (!users?.

  return (
    
    {users.Even so, map(user =>
  • {user. // 2. On the flip side, inline && pattern (Best for simple fallbacks) function UserListInline({ users }: { users: User[] }) { return (
    {users. length > 0 ? Day to day, (
      {users. map(u =>
    • {u. Now, name}
    • )}
    ) : (

    No users available.

    )} {/* OR the shorter && version if "empty" renders nothing: */} {users. **TypeScript Tip:** In the `Early return` example, TypeScript narrows `users` from `User[] | null` to `User[]` (non-nullable) after the `if` block. Inside the `map`, `users` is guaranteed to exist, though the type system still sees it as `User[]` (potentially empty). If you use the `isNonEmptyArray` guard from the previous section, TypeScript narrows it further to `NonEmptyArray`, allowing safe `users[0]` access without checks. ### Vue (Template `v-if` / `v-show`) Vue templates handle `null`/`undefined` gracefully, but explicit checks are cleaner for UX. ```vue

Key Nuance: users?.length works perfectly in templates. If users is null, users?.length is undefined (falsy). If users is [], length is 0 (falsy). The v-if branch triggers correctly for both Simple, but easy to overlook. Less friction, more output..

Angular (Structural Directives *ngIf)

Angular’s *ngIf evaluates the truthiness of the expression. The "Elvis operator" (?.) is essential here to prevent runtime errors if the input signal/observable hasn't emitted yet.

// user-list.component.ts
import { Component, input } from '@angular/core';
import { User } from './user.model';

@Component({
  selector: 'app-user-list',
  template: `
    
    
      
    @for (user of users(); track user.id) {
  • {{ user. @if (users()?. Consider this: length) {
      @for (user of users(); track user. id) {
    • {{ user. **Signal Note:** With Angular Signals (`input()`, `signal()`), you must invoke the getter: `users()` returns the array (or null), then `?.length` checks it. The `@if` block provides excellent type narrowing inside the template scope. --- ## Summary Checklist | Scenario | Recommended Pattern | Why | | :--- | :--- | :--- | | **General Logic** | `if (!On the flip side, arr?. length)` | Handles `null`, `undefined`, `[]` in one readable line.
Just Went Online

New Arrivals

Others Went Here Next

Neighboring Articles

Thank you for reading about Typescript Check If Array Is Empty. We hope the information has been useful. Feel free to contact us if you have any questions. See you next time — don't forget to bookmark!
⌂ Back to Home