# TypeScript: “Object is possibly 'null'” / “'x' is possibly 'undefined'”

> TS2531 Object is possibly null, TS2532 possibly undefined, TS18047 x is possibly null. Check for the value first, use ?. or ??, and keep ! for certain cases.

Canonical: https://app369.com/fix/typescript-object-is-possibly-null/
Last updated: 2026-11-08

The compiler reports Object is possibly 'null' (TS2531), Object is possibly 'undefined' (TS2532), or in newer versions 'el' is possibly 'null' (TS18047) and 'user' is possibly 'undefined' (TS18048). With strictNullChecks on, TypeScript tracks that document.getElementById, useRef<HTMLInputElement>(null), Array.find and optional properties can return nothing, and stops you from using the result as if they always return something.

**Short answer**

Handle the empty case before you use the value: an if check or an early return, optional chaining (el?.focus()) when doing nothing is fine, or ?? to fall back to a default. Use the non-null assertion (!) only when you know the value exists and TypeScript cannot see why. Do not turn strictNullChecks off; it is the check that catches Cannot read properties of null before your users do.

## **Where the null comes from, and the fix**

1. 01 ### **DOM lookups and React refs**
  > “When strictNullChecks is true, null and undefined have their own distinct types and you’ll get a type error if you try to use them where a concrete value is expected.” [**TypeScript, TSConfig: strictNullChecks**](https://www.typescriptlang.org/tsconfig/#strictNullChecks) document.getElementById and querySelector return null when nothing matches, and useRef<HTMLInputElement>(null) starts as null. Check before use.```
const form = document.getElementById('signup');
if (!form) return;
form.addEventListener('submit', onSubmit);

const inputRef = useRef<HTMLInputElement>(null);
inputRef.current?.focus();
```
2. 02 ### **Array.find, Map.get and optional properties**
  > “TypeScript follows possible paths of execution that our programs can take to analyze the most specific possible type of a value at a given position.” [**TypeScript, Narrowing**](https://www.typescriptlang.org/docs/handbook/2/narrowing.html) These return undefined when nothing is found. Decide what should happen then: throw, skip or default.```
const user = users.find((u) => u.id === id);
if (!user) throw new Error(\`No user ${id}\`);
sendEmail(user.email);

const name = profile.nickname ?? profile.fullName;
```
3. 03 ### **The check is in another function** TypeScript narrows within one function. A check done in a helper does not carry over unless the helper is a type guard. Return the checked value, or write the helper as a type predicate.```
function isDefined<T>(value: T | null | undefined): value is T {
  return value != null;
}
const ids = maybeIds.filter(isDefined); // string[]
```
4. 04 ### **The non-null assertion (!)** el!.focus() tells TypeScript the value is there and removes the check. It changes nothing at runtime, so if you are wrong the app still crashes. Use it only for values that are guaranteed, such as an element your own markup always renders.

Checked against the vendor’s docs on November 8, 2026: [TSConfig: strictNullChecks](https://www.typescriptlang.org/tsconfig/#strictNullChecks); [TypeScript Handbook: Narrowing](https://www.typescriptlang.org/docs/handbook/2/narrowing.html).

## **Rather hand it over?**

Fixed scope, fixed price, paid upfront. The work happens in writing; no call needed.

- [from $450**App Fix Ticket**One reproducible bug in an existing mobile or web app, diagnosed and fixed.**See scope and start**](https://app369.com/fixed-price/flutter-firebase-bug-fix/)
- [from $450**AI-Built App Rescue**A Lovable, Bolt or Cursor app checked end to end in 3 days, then its worst problems fixed at a fixed price.****No issues worth fixing, no charge.**** If the Launch Check finds nothing rated medium or higher, Simon refunds the full $450. You keep the report either way. This applies to the first 10 Launch Checks bought after October 30, 2026.**See scope and start**](https://app369.com/fixed-price/ai-app-rescue/)

Related: [**“Cannot read properties of undefined”**](https://app369.com/fix/cannot-read-properties-of-undefined/) · [**TypeScript “Argument of type is not assignable to parameter”**](https://app369.com/fix/typescript-argument-of-type-is-not-assignable-to-parameter/) · [**TypeScript “Property does not exist on type”**](https://app369.com/fix/typescript-property-does-not-exist-on-type/)

Questions

## **More on this error**

<details>

<summary>**Why did the message change to 'x' is possibly 'null'? **+</summary>

Newer TypeScript versions name the variable instead of saying Object, with new error codes (TS18047, TS18048). The meaning and the fixes are the same.

</details>

<details>

<summary>**Why does it still complain after I checked in useEffect? **+</summary>

The check has to happen in the same code path as the use. A check in one effect or callback does not narrow the value in another.

</details>

<details>

<summary>**Can you fix it for me? **+</summary>

If one bug is the whole problem, the App Fix Ticket covers it: a written diagnosis first, then the fix. If the app has more going on, the Launch Check is a written check of the whole app. Each issue found is rated critical, high, medium or low, with where it is and how it was confirmed. No issues worth fixing, no charge. If the Launch Check finds nothing rated medium or higher, Simon refunds the full $450. You keep the report either way. This applies to the first 10 Launch Checks bought after October 30, 2026.

</details>

A different error?

## **Describe it and get a written quote**

Send the error and what you were doing. You get a written scope, a fixed price and a timeline; Simon replies by email, usually within one business day.

[**Describe your problem **→](https://app369.com/contact/) [**All fixes**](https://app369.com/fix/)