100% Free Forever
AI-Powered Learning
Industry Expert Content
Certificates & Badges
Learn At Your Own Pace
TypeScript

Optional and Readonly Properties in TypeScript

Learn how to mark object properties as optional with `?` and immutable with `readonly`, and understand the compile-time-only nature of these guarantees.

Interfaces & Object TypesIntermediate9 min readJul 8, 2026
Analogies

1. Introduction

Real-world objects often have properties that aren't always present, or that should never change after creation. TypeScript provides two modifiers for interface (and type alias) members to express this: ? marks a property as optional, and readonly marks a property as unassignable after its initial value is set. Both are enforced only by the type checker, not at runtime.

🏏

Cricket analogy: A scorecard has a mandatory 'runs' field but an optional 'notOut' flag that only appears if the batter wasn't dismissed, while the 'matchDate' field is set once and never rewritten after the toss.

2. Syntax

typescript
interface Product {
  readonly sku: string;   // cannot be reassigned after creation
  name: string;
  discount?: number;      // may be omitted entirely
}

const item: Product = { sku: "A100", name: "Widget" };
// item.sku = "B200";   // Error: Cannot assign to 'sku' because it is read-only.
item.name = "Updated Widget"; // OK, not readonly

3. Explanation

An optional property (propertyName?: Type) means the property may be omitted from an object entirely. Its resolved type inside the interface becomes Type | undefined, so any code reading it must handle the possibility that the value is undefined before using it (often with optional chaining ?. or a guard).

🏏

Cricket analogy: If a player object has manOfTheMatch?: string, the field is string | undefined, so before printing the award you must check it exists, since a match without a standout performer leaves it unset.

A readonly property can be set once — typically when the object literal is created — but cannot be reassigned afterward. This is purely a compile-time check performed by the TypeScript compiler; it does not freeze the object at runtime. Nothing stops plain JavaScript, or TypeScript code using a type assertion, from mutating a 'readonly' property. readonly is also shallow: if a readonly property holds an object or array, that nested object's own properties remain fully mutable unless they too are marked readonly.

🏏

Cricket analogy: A readonly captainName is set once when the squad list is announced, but plain JavaScript or a type assertion could still overwrite it mid-series; and if the value is a nested homeGround object, its pitchType field stays mutable unless separately marked readonly.

Gotcha: readonly is not Object.freeze(). It only prevents *reassignment* through the TypeScript type system at compile time. At runtime, the property is an ordinary mutable JavaScript property, and a type assertion like (item as { sku: string }).sku = 'X' bypasses the check entirely. For genuine deep immutability at runtime you need Object.freeze() (shallow) or a dedicated immutability library.

4. Example

typescript
interface Config {
  readonly apiKey: string;
  timeout?: number;
}

function connect(config: Config): void {
  const effectiveTimeout = config.timeout ?? 5000;
  console.log(`Connecting with key ${config.apiKey}, timeout ${effectiveTimeout}ms`);
}

const config: Config = { apiKey: "secret-123" };
connect(config);

// readonly is compile-time only: an assertion can bypass it.
(config as { apiKey: string }).apiKey = "secret-999";
console.log(config.apiKey);

5. Output

text
Connecting with key secret-123, timeout 5000ms
secret-999

6. Key Takeaways

  • propertyName?: Type marks a property as optional; its effective type is Type | undefined.
  • readonly propertyName: Type prevents reassignment after initial creation, but only at compile time.
  • readonly does not deep-freeze objects — nested properties remain mutable, and type assertions can bypass the check.
  • Optional properties require handling undefined, often via optional chaining ?. or nullish coalescing ??.
  • For true runtime immutability, combine readonly with Object.freeze() or an immutability library.

Practice what you learned

Was this page helpful?

Topics covered

#TypeScript#TypeScriptProgrammingStudyNotes#Programming#OptionalAndReadonlyPropertiesInTypeScript#Optional#Readonly#Properties#Syntax#StudyNotes#SkillVeris