Question

TypeScript Optional Parameters: What Does the Question Mark Mean?

Question

Consider this TypeScript class:

export class Thread {
  id: string;
  lastMessage: Message;
  name: string;
  avatarSrc: string;

  constructor(
    id?: string,
    name?: string,
    avatarSrc?: string
  ) {
    this.id = id || uuid();
    this.name = name;
    this.avatarSrc = avatarSrc;
  }
}

What does the ? mean in a constructor parameter such as id?: string?

Short Answer

The ? marks a TypeScript parameter as optional. A caller may omit that argument, and inside the function its value can be undefined. You will learn how optional parameters affect types, argument order, defaults, and class property assignments.

Concept

id?: string is an optional parameter.

It means both of these calls are valid:

new Thread("thread-1", "General", "/images/general.png");
new Thread();

TypeScript treats an optional parameter as having a type that includes undefined:

id?: string
// behaves like:
id: string | undefined

This matters because a value might not have been supplied. Before using it as a definite string, your code must either:

  • provide a fallback value,
  • check whether it is defined, or
  • use a default parameter value.

Optional parameters are useful when an argument is genuinely not required for the function or constructor to do its job. In this class, an ID can be generated when one is not supplied.

Mental Model

Think of a function call as filling out a form.

A parameter without ? is a required field: you must fill it in.

function createUser(name: string) {}

A parameter with ? is an optional field: you may leave it blank.

function createUser(name?: string) {}

When the field is left blank, the function receives undefined. Your code must decide what to do with that missing value: use a default, skip an action, or report an error.

Take Quiz

Syntax and Examples

Optional parameters place ? after the parameter name and before its type:

function greet(name?: string): string {
  if (name === undefined) {
    return "Hello, guest!";
  }

  return `Hello, ${name}!`;
}

console.log(greet("Ava")); // "Hello, Ava!"
console.log(greet());       // "Hello, guest!"

A shorter approach is a default parameter:

function greet(name: string = "guest"): string {
  return `Hello, ${name}!`;
}

For the Thread constructor, defaults make the intended behavior explicit:

export class Thread {
  id: string;
  name?: string;
  avatarSrc?: string;

  constructor(
    id: string = uuid(),
    name?: string,
    avatarSrc?: string
  ) {
    this.id = id;
    this.name = name;
    this.avatarSrc = avatarSrc;
  }
}

Here, id is no longer optional from the constructor body's point of view: it will always be a string because uuid() is used when the caller omits it.

Step by Step Execution

Trace this constructor call:

const thread = new Thread(undefined, "Announcements");

Using this constructor:

constructor(
  id: string = uuid(),
  name?: string,
  avatarSrc?: string
) {
  this.id = id;
  this.name = name;
  this.avatarSrc = avatarSrc;
}
  1. The first argument is explicitly undefined.
  2. Because id has the default value uuid(), TypeScript/JavaScript calls uuid() and assigns its returned string to id.
  3. The second argument, "Announcements", is assigned to name.
  4. No third argument was provided, so avatarSrc is undefined.
  5. The properties receive these values:
thread.id;        // a generated UUID string
thread.name;      // "Announcements"
thread.avatarSrc; // undefined

Optionality applies to each argument by position. Omitting an earlier argument while supplying a later one requires passing undefined as a placeholder.

Real World Use Cases

Optional parameters appear whenever an API can use sensible defaults or an extra behavior is not always needed.

  • Generated identifiers: Accept an existing record ID during imports, but generate one for newly created records.
  • UI options: Let callers supply an optional avatar URL, CSS class, callback, or display label.
  • Search filters: Accept optional filters such as category, page number, sort order, or date range.
  • Logging: Allow an optional metadata object when recording an event.
  • API clients: Let callers provide optional request options, such as a timeout or authorization header.

Example with an options object:

type RequestOptions = {
  timeoutMs?: number;
  retry?: boolean;
};

function fetchPosts(options: RequestOptions = {}) {
  const timeout = options.timeoutMs ?? 5_000;
  const retry = options.retry ?? true;

  return { timeout, retry };
}

Optional properties are especially convenient when several settings may be omitted independently.

Take Quiz

Real Codebase Usage

In production code, optional parameters are often combined with clear defaults, validation, and guard clauses.

Prefer default values when a value is always needed

function createThread(id: string = uuid()): string {
  return id;
}

This states that the function always returns a usable ID.

Use guard clauses when omission is meaningful

function displayAvatar(avatarSrc?: string): string {
  if (!avatarSrc) {
    return "/images/default-avatar.png";
  }

  return avatarSrc;
}

Prefer an options object for many optional values

Positional optional arguments become hard to read:

createThread(undefined, "General", "/avatar.png");

An options object is clearer and easier to extend:

type ThreadOptions = {
  id?: string;
  name?: string;
  avatarSrc?: string;
};

class Thread {
  id: string;
  name?: string;
  avatarSrc?: string;

  constructor({ id = uuid(), name, avatarSrc }: ThreadOptions = {}) {
    this.id = id;
    this.name = name;
    this.avatarSrc = avatarSrc;
  }
}

const thread = new Thread({ name: "General" });

This pattern is common for constructors, framework configuration, and functions with multiple optional settings.

Common Mistakes

Assigning an optional value to a required property

In the original class, name and avatarSrc are declared as string, but the constructor parameters can be undefined.

class Thread {
  name: string;

  constructor(name?: string) {
    this.name = name; // Error with strictNullChecks enabled
  }
}

Fix it by making the property optional:

name?: string;

Or give it a default:

this.name = name ?? "Untitled";

Using || when an empty string is valid

this.id = id || uuid();

This generates a new ID when id is "", because an empty string is falsy. If only null and undefined should trigger the fallback, use ??:

this.id = id ?? uuid();

Putting a required parameter after an optional parameter

This is not allowed because callers could not reliably tell which argument they are omitting:

// Invalid
function connect(host?: string, port: number) {}

Put required parameters first, or use defaults/options:

function connect(port: number, host: string = "localhost") {}

Assuming optional means null

An omitted optional parameter becomes undefined, not null. A caller can still explicitly pass null only if the type allows it:

function setLabel(label?: string) {}

setLabel();      // valid: undefined
// setLabel(null); // invalid with strictNullChecks

Forgetting required class properties

lastMessage: Message; must be initialized in the constructor, declared optional, or definitely assigned later. With strict property initialization enabled, leaving it unassigned produces an error.

Comparisons

FeatureMeaningExampleBest use
Optional parameterCaller may omit the argument; value can be undefinedid?: stringAn input is not always needed
Default parameterUses a value when the caller passes or omits undefinedid: string = uuid()The function always needs a usable value
Union with undefinedExplicitly permits undefined, but does not make omission optionalid: string | undefinedThe caller must pass an argument positionally
Optional propertyAn object property may be absentavatarSrc?: stringData fields that are not always available
Nullable valueValue may be nullid: string | nullnull has a distinct meaning, such as “intentionally cleared”

An important difference:

function optionalValue(value?: string) {}
function requiredPosition(value: string | undefined) {}

optionalValue();          // valid
requiredPosition();       // error: an argument is required
requiredPosition(undefined); // valid
Take Quiz

Cheat Sheet

// Optional parameter
function save(title?: string) {}

// Equivalent value type inside the function
// title: string | undefined

// Default parameter
function save(title: string = "Untitled") {}

// Optional property
interface ThreadData {
  avatarSrc?: string;
}

// Fall back only for null or undefined
const actualId = id ?? uuid();

// Fall back for all falsy values, including "", 0, and false
const actualId = id || uuid();

Rules:

  • Write name?: Type, not name: ?Type.
  • Optional parameters normally come after required parameters.
  • An omitted optional argument is undefined.
  • Check for undefined or provide a default before treating an optional value as definitely present.
  • If a property can receive an optional parameter directly, make the property optional or assign a fallback value.
  • Use an options object when a function has several optional settings.

FAQ

What does ? mean after a TypeScript parameter name?

It makes the parameter optional. The caller can omit that argument, and the parameter may be undefined inside the function.

Is id?: string the same as id: string | undefined?

Inside the function, both values can be strings or undefined. At the call site, they differ: id?: string allows the argument to be omitted, while id: string | undefined requires an argument, even if it is undefined.

Can an optional parameter have a default value?

Yes, but ? is unnecessary when you use a default:

function createId(id: string = uuid()) {}

A parameter with a default is optional for callers.

Should I use || or ?? for an optional value?

Use ?? when only null and undefined should use the fallback. Use || only when all falsy values, including an empty string, should use it.

Why does assigning name?: string to name: string cause an error?

The parameter might be undefined, but the property promises it is always a string. Make the property optional or provide a string default.

Can I omit the second optional argument but provide the third one?

Yes, but pass undefined for the skipped position:

new Thread(undefined, undefined, "/avatar.png");

For readability, consider an options object instead.

Does an optional parameter accept null?

Not by default with strictNullChecks enabled. Use string | null | undefined if null should be accepted as well.

Take Quiz

Mini Project

Description

Build a small Thread model for a messaging application. The constructor should accept optional thread details while ensuring every thread receives an ID and a usable display name.

Goal

Create Thread instances with optional constructor inputs and safe default values.

Requirements

Use an optional id input and generate an ID when it is missing. Use an optional name and default it to "Untitled thread". Allow an avatar URL to be omitted. Create and log one thread with supplied values and one with no values.

Take Quiz

Keep learning

Related questions

View more