Skip to content

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

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

    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

    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 : TSConfig: strictNullChecks; TypeScript Handbook: Narrowing.

Rather hand it over?

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

Related: “Cannot read properties of undefined” · TypeScript “Argument of type is not assignable to parameter” · TypeScript “Property does not exist on type”

Questions

More on this error

Why did the message change to 'x' is possibly 'null'?

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

Why does it still complain after I checked in useEffect?

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.

Can you fix it for me?

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.

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.