Quick Overview
An index signature types keys that are not known in advance; a mapped type builds new keys from ones that are. `[K in keyof T]` is what makes `Partial` and `Readonly` ordinary library code rather than compiler magic.
In JavaScript, objects often act as lookup dictionaries with unknown dynamic property keys (for example, caching objects or user dictionaries).
An Index Signature defines the data type of dynamic keys and values for objects whose exact property names are not known ahead of time:
// Index signature: key must be string, value must be number
interface UserScores {
[username: string]: number;
}
const leaderboard: UserScores = {
alex: 100,
sarah: 250,
maria: 180,
};
leaderboard.david = 300; // Valid dynamic key addition!
A Mapped Type builds a new object shape by iterating over keys of an existing type using in keyof syntax:
type Permissions = "canRead" | "canWrite" | "canDelete";
// Mapped Type: Maps every permission string to a boolean!
type UserPermissionFlags = {
[K in Permissions]: boolean;
};
/*
Equivalent to:
type UserPermissionFlags = {
canRead: boolean;
canWrite: boolean;
canDelete: boolean;
};
*/
const myFlags: UserPermissionFlags = {
canRead: true,
canWrite: false,
canDelete: false,
};
You can add or remove readonly or optional ? modifiers inside mapped types using + or -:
// Make all properties optional
type Optional<T> = {
[K in keyof T]?: T[K];
};
leaderboard["unknownUser"]) returns number by default, even though it evaluates to undefined at runtime! Enable noUncheckedIndexedAccess: true in tsconfig.json to force number | undefined safety!Use mapped types for reusable type transformations. Next, let's enter Phase 3 and master Generics!
TypeScript erases entirely at build time, so a `string` annotation produces no runtime check whatsoever. A value arriving from an API is whatever the server actually sent, regardless of what the annotation claims.
`let x = "GET"` infers `string` because the binding can change; `const x = "GET"` infers the literal `"GET"`. That widening rule is why object properties need `as const` to stay narrow.
`string[]` is any number of strings; `[string, number]` is exactly two, in that order. `readonly` removes the mutating methods at compile time, so `push` becomes a type error rather than a runtime surprise.