Optional Properties in TypeScript

Optional properties let you mark object properties as not required. Add a question mark after the property name, and TypeScript knows the value might be undefined. Learn the syntax and safe patterns.

5 min read

TypeScript optional properties are properties in an object type that do not have to be present. You mark one with a question mark after the property name. When you read an optional property, TypeScript knows the value might be undefined, so it forces you to handle the missing case.

Optional properties are everywhere in real code. Configuration objects, API responses, and user input forms all have fields that may or may not exist. Instead of forcing every property to be required and filling missing ones with placeholders, you mark them as optional and let the compiler guide safe access. See object types in TypeScript for the foundation this builds on.

Marking a property as optional

Add a question mark after the property name in any object type. The syntax works the same on interfaces, type aliases, and inline annotations.

typescripttypescript
interface PaintOptions {
  shape: string;
  xPos?: number;
  yPos?: number;
}
 
function paint(opts: PaintOptions) {
  console.log(`Painting ${opts.shape}`);
}

The shape property is required. But xPos and yPos are optional -- callers can provide none, one, or both. Here are valid calls:

typescripttypescript
paint({ shape: "circle" });
paint({ shape: "square", xPos: 100 });
paint({ shape: "triangle", xPos: 50, yPos: 75 });

All three compile. If you provide xPos or yPos, the value must be a number. The question mark only affects whether the property can be omitted -- it does not change the type when present.

Reading optional properties safely

When you read an optional property under strict null checks, TypeScript widens the type to include undefined. You must narrow it before using it as if it were always present.

typescripttypescript
function paint(opts: PaintOptions) {
  console.log(opts.xPos.toFixed(2));
  // Error: 'opts.xPos' is possibly 'undefined'.
}

TypeScript is right. If the caller did not provide xPos, calling a method on undefined would crash at runtime. The fix is to check the value first with a conditional. Inside the if block, xPos is narrowed to number, so method calls are safe.

typescripttypescript
function paint(opts: PaintOptions) {
  if (opts.xPos !== undefined) {
    console.log(opts.xPos.toFixed(2)); // OK
  }
}

You can also use optional chaining for single property reads. When xPos is present, this prints the formatted number. When absent, it prints "no position":

typescripttypescript
function paint(opts: PaintOptions) {
  console.log(opts.xPos?.toFixed(2) ?? "no position");
}

Providing defaults with destructuring

The cleanest way to handle optional properties is destructuring with default values. This pattern removes undefined from the type entirely inside the function body.

typescripttypescript
function paint({ shape, xPos = 0, yPos = 0 }: PaintOptions) {
  console.log(`Painting ${shape} at (${xPos}, ${yPos})`);
}
 
paint({ shape: "circle" }); // "Painting circle at (0, 0)"

Because xPos and yPos have defaults of 0, they are always number inside the function. The caller can still omit them, but the function body never deals with undefined.

Note that destructuring patterns do not support type annotations on the individual variables. The notation shape: Shape in a destructuring pattern means "rename shape to Shape," not "type shape as Shape." Type the whole parameter object instead, as shown above.

Optional vs required-with-undefined

In most practical code, you want the optional form because it lets callers omit the property. Use the required-plus-undefined form only when you explicitly want to force callers to set the property to something, even if that something is undefined.

FormKey can be missingValue can be undefined
xPos?: numberYesYes
xPos: number | undefinedNo, the key must existYes

For more on handling potentially missing values, see null and undefined in TypeScript.

The option bag pattern

Optional properties power one of the most common TypeScript patterns: the option bag. Instead of a function with many positional parameters, you accept a single object where every field is optional.

typescripttypescript
interface FetchOptions {
  method?: string;
  body?: string;
  timeout?: number;
  retries?: number;
}

The interface declares every option as optional, so callers can pass whichever fields they need and skip the rest. Here is the function that consumes this options object:

typescripttypescript
function fetchData(url: string, opts: FetchOptions = {}) {
  const method = opts.method ?? "GET";
  const timeout = opts.timeout ?? 5000;
  console.log(`${method} ${url} (timeout: ${timeout}ms)`);
}

Inside the function, every option falls back to a sensible default using nullish coalescing. Callers provide only what they need:

typescripttypescript
fetchData("/api/users");
fetchData("/api/users", { method: "POST" });
fetchData("/api/users", { timeout: 10000, retries: 5 });

A typo in a property name is caught immediately because excess property checks reject unknown keys on object literals.

Common mistakes

Using an optional property without checking for undefined. With strict null checks enabled, this is a compile error. Always narrow first or use destructuring defaults.

Assuming an optional property is present because you always pass it. The type must be correct for every call site, not just the ones you remember. If a property is marked optional, the type says it can be absent.

Confusing optional with required-plus-undefined for object creation. The optional form lets callers omit the property. The required-plus-undefined form forces the key to exist. Choose the optional form unless you have a specific reason to force the property key to exist.

Rune AI

Rune AI

Key Insights

  • Add a question mark after a property name to mark it as optional in an object type.
  • Reading an optional property returns string | undefined, so check for undefined before use.
  • Optional properties can be omitted entirely, unlike properties typed with | undefined.
  • Destructuring with default values is the cleanest way to handle missing optional properties.
  • Optional properties are common in config objects, API responses, and option bags.
RunePowered by Rune AI

Frequently Asked Questions

Can I use optional properties on interfaces and type aliases?

Yes. Optional properties work the same way on interfaces, type aliases, and inline object types. Add a question mark after the property name in any object type definition.

What is the difference between optional and `| undefined`?

An optional property can be absent from the object entirely. A property typed as `string | undefined` must exist as a key, but its value can be undefined. In practice, the optional form also makes the property omissible when creating the object.

How do I provide a default value for an optional property?

Use destructuring with default values: `function f({ prop = 'default' }: { prop?: string })`. If the caller omits prop, it gets the default value inside the function body and the type narrows to string instead of `string | undefined`.

Conclusion

Optional properties let you model objects where some fields may or may not be present. Mark a property with a question mark, and TypeScript treats it as potentially undefined. Always check for undefined before using the value, or use destructuring defaults for a cleaner, narrower type inside function bodies.