Skip to content
TypeScript ts interfaces 4 min read

Optional Members

Not every property in an object is always present. TypeScript lets you mark an interface member as optional by appending ? to its name, signalling that callers may omit it. An optional member has the type T | undefined, so the compiler forces you to account for the missing case before you use the value. This page covers how to declare optional properties and methods, how optional differs from a T | undefined required member, and the patterns for reading optional values safely.

Declaring optional properties

Add ? after the property name to make it optional. The object is then valid whether or not the property is supplied.

interface UserProfile {
  id: number;
  name: string;
  bio?: string;
  avatarUrl?: string;
}

const minimal: UserProfile = { id: 1, name: "Alice" }; // ✅ ok
const full: UserProfile = {
  id: 2,
  name: "Bob",
  bio: "Engineer",
  avatarUrl: "https://example.com/bob.png",
};

Both objects satisfy UserProfile. The required members id and name must always be present, while bio and avatarUrl may be omitted entirely.

Optional members are T | undefined

When you read an optional property, its type includes undefined, so you cannot use it as if it were always present. The compiler rejects unguarded access to methods or properties on a possibly-undefined value.

function renderBio(profile: UserProfile): string {
  // ❌ Error: 'profile.bio' is possibly 'undefined'.
  return profile.bio.toUpperCase();
}

You must narrow the value first. The cleanest tools are optional chaining (?.) and the nullish coalescing operator (??), which together provide a fallback when the value is missing.

function renderBio(profile: UserProfile): string {
  return profile.bio?.toUpperCase() ?? "No bio provided";
}

Optional chaining short-circuits to undefined when bio is absent, and ?? substitutes the default, producing a guaranteed string.

Optional vs required-but-undefined

There is a subtle but important difference between an optional member and a required member whose type is T | undefined. An optional member may be omitted from the object; a required T | undefined member must be present, even if its value is undefined.

interface A { value?: string }            // may be omitted
interface B { value: string | undefined } // must be written, even as undefined

const a: A = {};                 // ✅ ok
const b1: B = {};                // ❌ Error: 'value' is missing
const b2: B = { value: undefined }; // ✅ ok
DeclarationCan omit key?Value type when readUse case
value?: stringYesstring | undefinedTruly optional fields
value: string | undefinedNostring | undefinedForce callers to acknowledge absence
value: stringNostringAlways-present fields

Under exactOptionalPropertyTypes, assigning undefined to an optional value?: string is rejected — TypeScript distinguishes “omitted” from “explicitly undefined”. Enable it when that distinction matters in your domain.

Optional methods

Methods can be optional too, which is common for callback-style configuration objects where each hook is independent.

interface LifecycleHooks {
  onStart?: () => void;
  onError?: (error: Error) => void;
  onFinish?: (durationMs: number) => void;
}

function run(task: () => void, hooks: LifecycleHooks): void {
  hooks.onStart?.();
  const start = performance.now();
  try {
    task();
  } catch (e) {
    hooks.onError?.(e as Error);
  } finally {
    hooks.onFinish?.(performance.now() - start);
  }
}

Calling hooks.onStart?.() invokes the method only if it was provided, avoiding a “not a function” error when the hook is absent.

Defaults for optional values

Optional members pair naturally with destructuring defaults. When you pull values out of an options object, supply a fallback so the rest of your code works with a guaranteed value.

interface RequestOptions {
  method?: "GET" | "POST";
  timeoutMs?: number;
  retries?: number;
}

function request(url: string, options: RequestOptions = {}) {
  const { method = "GET", timeoutMs = 5000, retries = 3 } = options;
  // method, timeoutMs, and retries are all non-undefined here
}

The defaults turn each optional value into a concrete one inside the function body, eliminating further guards.

Best Practices

  • Use ? for fields a caller may legitimately omit; keep genuinely required fields required.
  • Handle optional values with ?. and ?? rather than non-null assertions.
  • Prefer value?: T over value: T | undefined unless absence must be explicit.
  • Supply defaults via destructuring for optional options objects.
  • Consider exactOptionalPropertyTypes when “missing” and “undefined” mean different things.
  • Make optional methods callable with obj.method?.() to guard against absence.
  • Avoid making everything optional; over-optional interfaces lose their documentation value.
Last updated June 29, 2026
Was this helpful?