Skip to content

Optional and Readonly Properties

Not every property is always present, and not every property should be writable after creation. TypeScript gives you two modifiers for this: ? for optional properties and readonly for immutable ones.

Adding ? to a property name declares it as optional — the property may be present or absent:

interface SearchOptions {
amount: number;
difficulty?: Difficulty;
category?: number;
}

An object satisfies SearchOptions whether or not difficulty and category are present:

const basic: SearchOptions = { amount: 10 }; // ✓
const specific: SearchOptions = { amount: 10, difficulty: 'hard' }; // ✓
const full: SearchOptions = { amount: 10, difficulty: 'easy', category: 9 }; // ✓

When a property is optional, TypeScript widens its type to T | undefined. You must check before using it:

function buildUrl(options: SearchOptions): string {
let url = `https://opentdb.com/api.php?amount=${options.amount}&type=multiple`;
if (options.difficulty) {
url += `&difficulty=${options.difficulty}`; // safe — checked first
}
return url;
}

Without the if guard, TypeScript would report an error: you cannot use options.difficulty directly because it might be undefined.

The readonly modifier prevents a property from being reassigned after the object is created:

interface TriviaQuestion {
readonly question: string;
readonly category: string;
readonly difficulty: Difficulty;
readonly correctAnswer: string;
allAnswers: string[];
}

question, category, difficulty, and correctAnswer are fixed at creation — they describe the question as it came from the API and should not change. allAnswers is mutable because the array is shuffled during normalization.

Attempting to reassign a readonly property is a compile error:

const q: TriviaQuestion = { /* ... */ };
q.correctAnswer = 'Mars'; // Error: Cannot assign to 'correctAnswer' because it is a read-only property

You can mark an array property as readonly to prevent mutation of the array itself:

interface TriviaQuestion {
readonly allAnswers: readonly string[];
}

readonly string[] (or equivalently ReadonlyArray<string>) prevents push, pop, splice, and direct index assignment. The answers can be read but not modified after the question is created.

const prevents a variable from being reassigned. readonly prevents a property from being reassigned. They are different constraints:

const q = { correctAnswer: 'Mercury' };
q.correctAnswer = 'Mars'; // ✓ — const prevents reassigning q, not its properties
interface Fixed { readonly correctAnswer: string; }
const q2: Fixed = { correctAnswer: 'Mercury' };
q2.correctAnswer = 'Mars'; // Error — readonly prevents changing the property

Use const for variables, readonly for properties.

Both modifiers can appear on the same property:

interface HighScore {
score: number;
total: number;
date: string;
readonly id?: string; // optional and immutable if present
}

In AceIt’s types.ts, TriviaQuestion uses readonly on the fields that come directly from the API. Once a question is normalized, its text, category, difficulty, and correct answer are facts — they should not change:

interface TriviaQuestion {
readonly question: string;
readonly category: string;
readonly difficulty: Difficulty;
readonly correctAnswer: string;
readonly allAnswers: string[];
}

This communicates intent: TriviaQuestion is a value object, not a mutable record. If any code tried to “fix” a wrong answer in place, the compiler would stop it.

Update the interfaces in types.ts:

  1. Add readonly to question, category, difficulty, correctAnswer, and allAnswers in TriviaQuestion.
  2. Add readonly to all properties in HighScore — once a score is recorded, none of its fields should change.
  3. Create an interface FetchOptions with amount: number (required), and difficulty?: Difficulty and category?: number (both optional).
  4. Write a function buildUrl(options: FetchOptions): string that constructs the Open Trivia DB URL, appending optional parameters only when present.
  • ? marks a property as optional — it may be present or absent. TypeScript widens its type to T | undefined and requires a check before use.
  • readonly prevents a property from being reassigned after creation.
  • readonly on an array property does not prevent mutation of the array — use readonly T[] or ReadonlyArray<T> to prevent that too.
  • const prevents variable reassignment; readonly prevents property reassignment — they are different constraints.
  • Both ? and readonly can appear on the same property.