Typorithm · easy

6. Nullability, optional fields and indexed reads

typescriptstable-typescriptinterviewindependent-practice
Saved on this device
Full lesson · 4 learning steps Open any exercise for focused practice
1

Step 1 of 4 · intro card

6. Nullability, optional fields and indexed reads

0%

Concept and contract

Handle absence where it occurs instead of hiding it with non-null assertions.

With strictNullChecks, null and undefined are distinct from ordinary strings and objects. An optional field introduces possible absence at read time. A dictionary or array access may also miss, even when the element type describes every value that actually exists.

noUncheckedIndexedAccess makes that uncertainty visible for unchecked indexed reads. It is not included merely by enabling strict. exactOptionalPropertyTypes separately distinguishes an absent optional property from a present undefined property. Pin both options so readers and CI share the same contract.

Mechanism and worked example

The example returns a fallback when the first item is absent.

The example returns a fallback when the first item is absent. Optional chaining protects access on a nullish receiver, while nullish coalescing falls back only for null or undefined. This deliberately preserves an empty title string rather than treating it as missing.

An array length check does not universally turn every dynamic index into a guaranteed element. Prefer iteration, a checked local variable, or an explicit nonempty tuple contract when a function genuinely requires at least one item. Choose the model that matches the input guarantee.

Complete worked example

typescript

type Role = { title: string };
function firstTitle(items: readonly Role[]): string {
  return items[0]?.title ?? "No roles";
}
Reasoning
The fallback responds to absence while preserving an intentionally empty title.

Counterexamples and common mistakes

The rejected assignment exposes that items[0] may not exist.

The rejected assignment exposes that items[0] may not exist. Adding an exclamation mark would silence the warning without inserting a runtime check. The same issue occurs with Record<string, Value>: an arbitrary key may have no corresponding stored value.

Do not replace every missing value with an empty object. That can erase the distinction between unavailable data and a valid empty result. Likewise,

is unsuitable when zero, false or an empty string must remain meaningful; use ?? when only nullish absence triggers fallback.

Rejected counterexample: expected compiler diagnostic

typescript

const values: string[] = [];
// @ts-expect-error noUncheckedIndexedAccess exposes absence.
const first: string = values[0];
Reasoning
The extra indexed-access option prevents an empty array from masquerading as a guaranteed string.

Independent practice

Implement lastTitle for a readonly array of title objects.

Implement lastTitle for a readonly array of title objects. Return No roles for an empty array, preserve an empty title, and accept a nonempty normal array. Use a local indexed value and a nullish fallback rather than a non-null assertion.

Also create a configuration patch that omits an optional limit when no limit was supplied. Contrast omission with assigning undefined under exactOptionalPropertyTypes. This second case tests write-time semantics, while the lastTitle function tests uncertain reads.

Step 1: Read the final element into a local. Step 2: Use a nullish fallback without losing empty text.

Practice starter: solve before reading the solution

typescript

function lastTitle(items: readonly { title: string }[]): string { throw new Error("TODO"); }
Reasoning
Use one local read and distinguish omission from a present optional field.
  • Hint 1: An empty array produces an absent final item.
  • Hint 2: Omit an optional limit when it is unavailable.

Solution, tests and trade-offs

The solution reads the final element once, then uses optional chaining and ??.

The solution reads the final element once, then uses optional chaining and ??. The empty array produces an index of minus one, which yields undefined in ordinary indexed access; the fallback handles it without pretending the array is nonempty.

Check an empty array, an empty string title and a normal title. Compile a separate negative optional-field assignment. These tests prevent a later refactor from accidentally replacing ?? with

or introducing an assertion that makes the successful case look simpler while breaking absence.

Practice solution and acceptance checks

typescript

function lastTitle(items: readonly { title: string }[]): string {
  const last = items[items.length - 1];
  return last?.title ?? "No roles";
}
type Patch = { limit?: number };
function patch(limit: number | undefined): Patch {
  return limit === undefined ? {} : { limit };
}

// Acceptance checks
if(lastTitle([])!=="No roles"||lastTitle([{title:"A"},{title:""}])!==""||"limit" in patch(undefined)||patch(0).limit!==0)throw new Error("nullability");
Reasoning
The output contract matches both empty-array behavior and exact optional-property writes.

Interview reasoning and next step

For interviews, explain why strict and noUncheckedIndexedAccess are separate controls.

For interviews, explain why strict and noUncheckedIndexedAccess are separate controls. Describe when a tuple can encode nonemptiness and when a runtime guard is the honest solution. Avoid saying that array indexing is always safe after any length check.

Next, type function contracts so callers can see whether absence is expected, rejected or handled. A return union such as string | undefined is often more accurate than a function that claims string but occasionally returns nothing.

Optional stretch — after the core task works: Write a separate nonempty tuple version and compare callers.

Handle absence where it occurs instead of hiding it with non-null assertions.