Type expression

null, undefined and nullable types

  • Basics
  • Tested with TypeScript 5.9

With strict (or strictNullChecks), null and undefined are not part of any other type. A value that may be absent says so in its type, {string | null}, and TypeScript makes you check it before you call methods on it.

The checks matter most where JavaScript returns nothing on a miss: document.querySelector returns null, Array.prototype.find and Map.prototype.get return undefined. The examples show the errors for each (TS18047, TS18048) and the checks that clear them.

At a glance

Use JSDoc in a .js file TypeScript in a .ts file
Nullable /** @type {string | null} */ : string | null
Closure shorthand /** @type {?string} */ : string | null
May be missing /** @type {string | undefined} */ : string | undefined
Optional parameter /** @param {string} [note] */ (note?: string)

Examples

cart.js Error confirmed 5.9.3 TypeScript 5.9.3, run 21 October 2026: cart.js(6,1): error TS18048: 'mug' is possibly 'undefined'. 6.0.3 TypeScript 6.0.3, run 21 October 2026: the same as 5.9.3. cart.js(6,1): error TS18048: 'mug' is possibly 'undefined'. 7.0.2 TypeScript 7.0.2, run 21 October 2026: the same as 5.9.3. cart.js(6,1): error TS18048: 'mug' is possibly 'undefined'. checked 21 October 2026
/** @type {{ sku: string, quantity: number }[]} */
const lines = [{ sku: "TEE-01", quantity: 2 }];

const mug = lines.find((line) => line.sku === "MUG-07");
mug.quantity += 1;error TS18048: 'mug' is possibly 'undefined'.

const tee = lines.find((line) => line.sku === "TEE-01");
if (tee) tee.quantity += 1;

export { lines };
find() can return undefined.
jsconfig.json for this example
{
  "compilerOptions": {
    "checkJs": true,
    "noEmit": true,
    "strict": true,
    "target": "es2022",
    "module": "nodenext",
    "types": []
  },
  "include": ["*.js"]
}
total.js Error confirmed 5.9.3 TypeScript 5.9.3, run 21 October 2026: total.js(3,1): error TS18047: 'total' is possibly 'null'. 6.0.3 TypeScript 6.0.3, run 21 October 2026: the same as 5.9.3. total.js(3,1): error TS18047: 'total' is possibly 'null'. 7.0.2 TypeScript 7.0.2, run 21 October 2026: the same as 5.9.3. total.js(3,1): error TS18047: 'total' is possibly 'null'. checked 21 October 2026
const total = document.querySelector("#cart-total");
total.textContent = "$39.00";error TS18047: 'total' is possibly 'null'.

const badge = document.querySelector("#cart-count");
if (badge) badge.textContent = "2";

export {};
querySelector can return null.
jsconfig.json for this example
{
  "compilerOptions": {
    "checkJs": true,
    "noEmit": true,
    "strict": true,
    "target": "es2022",
    "module": "nodenext",
    "types": []
  },
  "include": ["*.js"]
}
note.js note.ts TypeScript 5.9.3 TypeScript 5.9.3, run 21 October 2026: No errors. 6.0.3 TypeScript 6.0.3, run 21 October 2026: the same as 5.9.3. No errors. 7.0.2 TypeScript 7.0.2, run 21 October 2026: the same as 5.9.3. No errors. checked 21 October 2026
/**
 * @param {string | null} note
 * @param {?string} giftMessage the same type, in the older Closure spelling
 * @returns {string}
 */
export function packingSlip(note, giftMessage) {
  const lines = [note ?? "No note"];
  if (giftMessage !== null) lines.push(`Gift: ${giftMessage}`);
  return lines.join("\n");
}

packingSlip(null, "Happy birthday");
packingSlip("Leave at door", null);
export function packingSlip(note: string | null, giftMessage: string | null): string {
  const lines = [note ?? "No note"];
  if (giftMessage !== null) lines.push(`Gift: ${giftMessage}`);
  return lines.join("\n");
}

packingSlip(null, "Happy birthday");
packingSlip("Leave at door", null);
A parameter that may be null, two spellings.
jsconfig.json for this example
{
  "compilerOptions": {
    "checkJs": true,
    "noEmit": true,
    "strict": true,
    "target": "es2022",
    "module": "nodenext",
    "types": []
  },
  "include": ["*.js"]
}

null or undefined?

Use undefined for "not provided" (optional parameters and properties, missing map entries) and null for "deliberately empty" (a cleared field, an API's empty value). TypeScript treats them as different types: a string | null parameter does not accept undefined.

{?string} is the Closure Compiler shorthand for {string | null}. TypeScript reads it that way, but prints the long form in its messages, so the long form is easier to match up.

Checking before use

if (value), if (value !== null), optional chaining value?.name and the ?? operator all narrow or handle the empty case. Inside the if, the type no longer includes null or undefined.

JavaScript files cannot use TypeScript's ! operator to assert a value is present; the type cast and assertion function alternatives are covered under non-null assertions.

Gotchas

  • Without strict or strictNullChecks, null and undefined are assignable to everything and {?string} adds nothing. Turn on strict before relying on these types.
  • if (count) also skips 0 and if (name) skips the empty string. Compare with !== undefined when those are valid values.

Questions

How do I make a type nullable in JSDoc?

Add null as a union member: {string | null}. The shorthand {?string} means the same.

Sources

Last reviewed 20 October 2026.