How to fix Cannot read properties of null (reading 'x') in TypeScript

TypeScriptBEGINNERHIGHUpdated Dec 8, 2025· 425 views

This runtime error occurs when your code attempts to access a property or method on a value that is null. It's one of the most common JavaScript/TypeScript runtime errors and can be prevented using null checks, optional chaining, or TypeScript's strict null checking.

Quick fix

Fixes Cannot read properties of null (reading 'x')

// BEFORE: Error - Cannot read properties of null
const user = getUserById(id); // Returns null if not found
console.log(user.name);

// AFTER: Check for null first
const user = getUserById(id);
if (user !== null) {
  console.log(user.name);
} else {
  console.log("User not found");
}
  1. Add null checks before accessing properties
  2. Use optional chaining (?.) operator
  3. Enable TypeScript strictNullChecks
  4. Fix DOM element access timing issues
typescript — error
// BEFORE: Error - Cannot read properties of null
const user = getUserById(id); // Returns null if not found
console.log(user.name);
// AFTER: Check for null first
const user = getUserById(id);
if (user !== null) {
console.log(user.name);
} else {
console.log("User not found");
}
Cannot read properties of null (reading 'x')

What this error means

This is a runtime TypeError that occurs when you try to access a property, call a method, or use any member of a value that evaluates to null. Unlike compile-time TypeScript errors, this error happens during code execution. The error message follows the pattern "Cannot read properties of null (reading 'propertyName')" where 'propertyName' is the specific property you attempted to access. This error is nearly identical to "Cannot read properties of undefined" but specifically indicates the value was explicitly null rather than undefined. Common scenarios include: 1. **DOM element not found**: Using `document.getElementById()` or `querySelector()` that returns null because the element doesn't exist 2. **Asynchronous data**: Accessing properties on data that hasn't loaded yet from an API call 3. **Uninitialized object properties**: Accessing nested properties when intermediate objects are null 4. **Array methods**: Using methods like `.find()` which return null when no match is found, then accessing properties on the result The error occurs because null is a primitive value with no properties or methods, so any attempt to read from it throws a TypeError.

Typical symptoms

  • Browser console shows 'TypeError: Cannot read properties of null'
  • Application crashes or stops executing at the error point
  • White screen or broken UI in React applications when component renders
  • Error occurs intermittently, especially after page load or during async operations
  • TypeScript code compiles successfully but fails at runtime
  • Error message includes specific property name being accessed (e.g., reading 'map', 'length', 'addEventListener')

Common causes

  1. Accessing DOM elements before they're rendered (script runs before HTML loads)
  2. Misspelled element ID or selector in document.getElementById() or querySelector()
  3. Attempting to access properties on async data before it's fetched
  4. Array.find() or similar methods return null/undefined, then accessing properties without checking
  5. Object properties initialized to null and accessed before assignment
  6. Optional chaining not used when accessing nested properties on potentially null objects
  7. API responses return null for missing data, code doesn't handle null case
  8. React component tries to access props.children or other props that can be null
  9. TypeScript strictNullChecks disabled, allowing null values to bypass type checking

How to fix "Cannot read properties of null (reading 'x')"

1Add null checks before accessing properties

The simplest fix is to check if a value is null or undefined before accessing its properties:

typescript
// BEFORE: Error - Cannot read properties of null
const user = getUserById(id); // Returns null if not found
console.log(user.name);

// AFTER: Check for null first
const user = getUserById(id);
if (user !== null) {
  console.log(user.name);
} else {
  console.log("User not found");
}

For DOM elements:

typescript
// BEFORE: Error if element doesn't exist
const button = document.getElementById("submit-btn");
button.addEventListener("click", handleClick);

// AFTER: Null check
const button = document.getElementById("submit-btn");
if (button !== null) {
  button.addEventListener("click", handleClick);
}

You can also use combined null/undefined checks:

typescript
if (user != null) { // Checks both null and undefined
  console.log(user.name);
}
2Use optional chaining (?.) operator

Optional chaining (introduced in TypeScript 3.7 / ES2020) safely accesses nested properties and short-circuits to undefined if any step is null:

typescript
// BEFORE: Error if user or address is null
const city = user.address.city;

// AFTER: Returns undefined instead of throwing error
const city = user?.address?.city;

Combined with nullish coalescing (??), you can provide default values:

typescript
// Return "Unknown" if user, address, or city is null/undefined
const city = user?.address?.city ?? "Unknown";

// Array method example
const firstItem = items?.find(item => item.id === 5);
const itemName = firstItem?.name ?? "Not found";

// DOM element example
const buttonText = document.getElementById("btn")?.textContent;

For optional method calls:

typescript
// Only call if handler exists
handler?.onClick?.();

// Array methods
const uppercaseNames = users?.map(u => u.name.toUpperCase());
3Enable TypeScript strictNullChecks

TypeScript's strictNullChecks compiler option catches null/undefined errors at compile time:

json
// tsconfig.json
{
  "compilerOptions": {
    "strict": true,  // Includes strictNullChecks
    // OR enable specifically:
    "strictNullChecks": true
  }
}

With strict null checks enabled, TypeScript will error if you don't handle null cases:

typescript
// TypeScript will show error: Object is possibly 'null'
const element = document.getElementById("myId");
element.addEventListener("click", handler); // Error!

// Fix: Add null check or use optional chaining
const element = document.getElementById("myId");
element?.addEventListener("click", handler); // OK

// Or with explicit check
if (element !== null) {
  element.addEventListener("click", handler); // OK
}

You can also use type narrowing:

typescript
function processUser(user: User | null) {
  if (user === null) {
    return; // Early return
  }
  // TypeScript knows user is not null here
  console.log(user.name);
}
4Fix DOM element access timing issues

Ensure your script runs after the DOM is ready. Common patterns:

Move script tag to end of body:

html
<!DOCTYPE html>
<html>
  <body>
    <button id="myButton">Click me</button>

    <!-- Script runs AFTER elements are rendered -->
    <script src="app.js"></script>
  </body>
</html>

Use DOMContentLoaded event:

typescript
document.addEventListener("DOMContentLoaded", () => {
  const button = document.getElementById("myButton");
  if (button !== null) {
    button.addEventListener("click", handleClick);
  }
});

In React, use useEffect for DOM access:

typescript
import { useEffect } from "react";

function MyComponent() {
  useEffect(() => {
    const element = document.getElementById("external-element");
    if (element !== null) {
      element.style.color = "blue";
    }
  }, []); // Runs after component mounts

  return <div>Component</div>;
}

Check if selector is correct:

typescript
// Returns null if ID is wrong
const btn = document.getElementById("sumbit-btn"); // Typo!

// Should be:
const btn = document.getElementById("submit-btn");
5Handle async data properly

For data loaded asynchronously, initialize with null and check before rendering:

React example with loading states:

typescript
function UserProfile({ userId }: { userId: string }) {
  const [user, setUser] = useState<User | null>(null);
  const [loading, setLoading] = useState(true);

  useEffect(() => {
    fetchUser(userId)
      .then(data => setUser(data))
      .finally(() => setLoading(false));
  }, [userId]);

  if (loading) return <div>Loading...</div>;
  if (user === null) return <div>User not found</div>;

  // Safe to access user properties here
  return <div>{user.name}</div>;
}

Vanilla TypeScript with async/await:

typescript
async function displayUserData(userId: string) {
  const user = await fetchUser(userId);

  if (user === null) {
    console.log("User not found");
    return;
  }

  // Safe to use user here
  console.log(user.name);
  updateUI(user);
}

Provide default values:

typescript
const response = await fetch("/api/config");
const config = await response.json() ?? { theme: "light" };
console.log(config.theme); // Safe, always has default
6Use non-null assertion operator (!) with caution

TypeScript's non-null assertion operator (!) tells the compiler "I know this isn't null." Use sparingly and only when you're certain:

typescript
// Tell TypeScript this element definitely exists
const button = document.getElementById("myButton")!;
button.addEventListener("click", handleClick);

Warning: This bypasses TypeScript's safety checks. If the element doesn't exist, you'll still get the runtime error. Only use when:

1. You're absolutely certain the value isn't null
2. The element is guaranteed to exist (e.g., in a specific HTML template)
3. You've already validated it exists earlier in the code

Better alternatives:

typescript
// Instead of non-null assertion, use type guard
function initializeButton() {
  const button = document.getElementById("myButton");
  if (button === null) {
    throw new Error("Button element not found"); // Fail fast
  }

  // TypeScript knows button is not null here
  button.addEventListener("click", handleClick);
}

Or use optional chaining for graceful handling:

typescript
// Doesn't throw, just skips if null
document.getElementById("myButton")?.addEventListener("click", handleClick);

Advanced notes

### Difference Between null and undefined

Both cause similar errors, but they have different meanings:

- null: Explicitly assigned to indicate "no value" or "empty"
- undefined: Variable declared but not initialized, or property doesn't exist

typescript
let userA = null;       // Intentionally empty
let userB;              // Undefined (no assignment)
let userC = undefined;  // Explicitly undefined

const obj = { name: "John" };
console.log(obj.age);   // undefined (property doesn't exist)

### TypeScript Type Guards and Narrowing

TypeScript can infer when null has been checked:

typescript
function processUser(user: User | null) {
  // Type is User | null here

  if (user === null) {
    return;
  }

  // TypeScript narrows type to User (not null) here
  console.log(user.name); // No error, TypeScript knows user isn't null
}

Custom type guards for complex checks:

typescript
function isValidUser(user: User | null): user is User {
  return user !== null && user.id !== undefined;
}

const user = getUserById(5);
if (isValidUser(user)) {
  console.log(user.name); // TypeScript knows user is User here
}

### React Specific: Props and State

Common React scenarios where null appears:

typescript
// Props with optional children
interface Props {
  children?: React.ReactNode; // Can be undefined
}

function Container({ children }: Props) {
  // Error if you don't check
  return <div>{children.toUpperCase()}</div>;

  // Fix: Check or use optional chaining
  return <div>{children ?? "No content"}</div>;
}

### Array.find() Returns undefined (Not null)

Note that .find() returns undefined, not null, but the error is the same:

typescript
const users = [{ id: 1, name: "Alice" }];
const user = users.find(u => u.id === 999); // Returns undefined
console.log(user.name); // Error: Cannot read properties of undefined

// Fix
const user = users.find(u => u.id === 999);
if (user !== undefined) {
  console.log(user.name);
}
// Or use optional chaining
console.log(user?.name);

### Nullish Coalescing vs Logical OR

The ?? operator only checks for null/undefined, while || checks for any falsy value:

typescript
const count = 0;

// Logical OR treats 0 as falsy
const result1 = count || 10; // 10 (wrong if 0 is valid)

// Nullish coalescing only checks null/undefined
const result2 = count ?? 10; // 0 (correct)

Use ?? when 0, "", or false are valid values.

### Server-Side Rendering (SSR) Considerations

In Next.js or SSR environments, window/document might not exist:

typescript
// Causes error on server
const width = window.innerWidth; // Error: window is not defined

// Fix: Check if running in browser
const width = typeof window !== "undefined" ? window.innerWidth : 0;

// Or use useEffect (client-side only)
useEffect(() => {
  const width = window.innerWidth; // Safe in useEffect
}, []);

Frequently asked questions

What does "Cannot read properties of null (reading 'x')" mean?
This is a runtime TypeError that occurs when you try to access a property, call a method, or use any member of a value that evaluates to null. Unlike compile-time TypeScript errors, this error happens during code execution. The error message follows the pattern "Cannot read properties of null (reading 'propertyName')" where 'propertyName' is the specific property you attempted to access.
How do I fix "Cannot read properties of null (reading 'x')"?
This runtime error occurs when your code attempts to access a property or method on a value that is null. It's one of the most common JavaScript/TypeScript runtime errors and can be prevented using null checks, optional chaining, or TypeScript's strict null checking.
What causes "Cannot read properties of null (reading 'x')"?
Accessing DOM elements before they're rendered (script runs before HTML loads) Misspelled element ID or selector in document.getElementById() or querySelector() Attempting to access properties on async data before it's fetched
Is the "Cannot read properties of null (reading 'x')" error serious?
Yes. This is a high-severity error that usually blocks work until it is fixed, but the steps above resolve it.

Official resources & further reading