Optional, readonly & Index Signatures
Intermediate · 21 min read · ▶ live playground · ✦ checkpoint
Optional properties, readonly, and index signatures all answer one object-shape question: what can be missing, what can be changed, and what happens when the key is not known ahead of time? Use ? for properties that may be omitted, readonly for values you don't want changed through this shape, and index signatures only when the object is truly a dictionary.
You already know object shapes are promises. This lesson sharpens three promises that look small and cause real bugs when they are too loose.
Optional means the key may be missing
An optional property is a property marked with ?, meaning the key may be omitted from the object. A property written as string | undefined is different: the key must exist, but its value may be either a string or undefined.
That difference is about property presence, which means whether the key exists on the object at all:
Both reads can give you undefined, so both need a check before you call string methods. The shape promise is still different: OptionalCoupon allows "no code key"; CouponSlot requires a code key whose value can be missing.
With this course's current checker settings, an optional property also accepts an explicit undefined value. The durable rule is still presence: ? says "the key may be absent."
Here is the same distinction as a static diagnostic:
type OptionalLaunchWindow = {
window?: string;
};
const optionalWindow: OptionalLaunchWindow = {};
type RequiredLaunchWindow = {
window: string | undefined;
};
const missingWindow: RequiredLaunchWindow = {};→ playground.ts:11:7 - error TS2741: Property 'window' is missing in type '{}' but required in type 'RequiredLaunchWindow'.
readonly is a type-layer lock
A mutable value is a value that can be changed after it is created. A readonly property says code using this shape may read the property, but may not assign a new value to it.
ReadonlyArray<string> means a read-only array of strings. You can read and loop over it, but methods that would change the array, like push, are not available through that type. Lesson 7.1 explains the angle-bracket pattern in depth; for now, read it as "array of strings, read-only from this view."
Use readonly when a function should not edit the object it receives, or when a returned object should be treated as a snapshot. If you need actual runtime freezing, that is a JavaScript operation, not something TypeScript inserts for you.
Index signatures describe dictionaries
An index signature is the [key: string]: valueType part of an object type. It says "for any string key you use on this object, the value follows this type."
That is useful for dictionary objects, objects whose keys are not known ahead of time:
The danger is hiding in the output: Kai was never in the object, so JavaScript returned undefined, and arithmetic turned it into NaN. The type said every string key gives a number. Runtime said a missing key gives nothing useful.
So use index signatures for real dictionaries, not for fixed shapes where you already know the property names. If your object has name, plan, and karma, write those properties. An index signature is a wide promise: it covers every possible string key.
noUncheckedIndexedAccess makes lookups honest
noUncheckedIndexedAccess is a compiler option, a switch in tsconfig.json that changes how strict the checker is. With it on, karma["Kai"] has type number | undefined, because a key lookup can miss.
The live playgrounds in this lesson use the course's baseline checker settings, so this flag is shown as project config rather than a live playground toggle:
{
"compilerOptions": {
"strict": true,
"noUncheckedIndexedAccess": true
}
}With that option enabled, this assignment is no longer accepted:
type KarmaByMember = {
[memberName: string]: number;
};
const karma: KarmaByMember = { Mina: 42 };
const kaiKarma: number = karma["Kai"];→ with noUncheckedIndexedAccess: playground.ts:6:7 - error TS2322: Type 'number | undefined' is not assignable to type 'number'.
Type 'undefined' is not assignable to type 'number'.
That flag is usually the honest setting for serious projects. It makes dictionary lookups behave like optional reads: check for undefined, then use the value.
This code is safe with or without the flag:
You now have sharper object tools: keys that can be absent, read-only views, and dictionaries that don't pretend every possible key exists. Next lesson combines object shapes with extends and intersections, where two promises meet in one shape.
Checkpoint
Answer all three to mark this lesson complete