Dynamic object keys
1. Use an index signature
This is the most basic and common way.
interface Dictionary {
[key: string]: string;
}
const colors: Dictionary = {
red: "#ff0000",
green: "#00ff00",
blue: "#0000ff",
};Here
[key: string]: stringmeans: the object can have any number of properties with keys of typestring, and all the values must be strings.
Variants of index signatures
| Signature | What it means |
|---|---|
[key: string]: string | keys are strings, values are strings |
[key: number]: boolean | keys are numbers, values are booleans |
[key: string]: any | keys are any strings, values are of any type |
[key: string]: SomeType | values match a custom type |
An example with a custom type
interface UserScores {
[username: string]: number;
}
const scores: UserScores = {
Tim: 90,
Max: 75,
John: 82,
};TypeScript checks that all the values are numbers.
2. Use a generic type with Record
TypeScript provides the utility type Record<K, T> -
a convenient way to describe objects with dynamic keys of a certain type.
type Dictionary = Record<string, string>;
const translations: Dictionary = {
hello: "Hola",
bye: "Adios",
};
Record<K, T>literally means: "an object where each key has typeKand the value has typeT".
Example: typed keys
type Languages = "en" | "fr" | "de";
type LocaleMap = Record<Languages, string>;
const appName: LocaleMap = {
en: "Shop",
fr: "Boutique",
de: "Geschaft",
};TypeScript guarantees that all three keys (
en,fr,de) are present, and that no others can be.
3. Describing an object with dynamic keys plus extra fields
You can combine specific properties with an index signature.
interface ProductInfo {
id: number;
name: string;
[extraField: string]: string | number;
}
const product: ProductInfo = {
id: 10,
name: "Shirt",
color: "white",
price: 49,
};TypeScript lets you add any fields, but the values must be string or number (per the index signature).
4. Dynamic keys computed from a type
TypeScript can build objects with keys derived from a union type:
type Status = "loading" | "success" | "error";
type StatusMessages = {
[K in Status]: string;
};
const messages: StatusMessages = {
loading: "Loading...",
success: "Success!",
error: "Error!",
};This uses a mapped type - TypeScript generates an object with keys
"loading" | "success" | "error"and values of typestring.
5. If you need partial (optional) dynamic keys
You can add Partial<>:
type FeatureFlags = Partial<Record<"darkMode" | "beta" | "ads", boolean>>;
const flags: FeatureFlags = {
darkMode: true, // allowed
// other keys can be left out
};6. The difference between Record and an index signature
| Property | Record | Index signature |
|---|---|---|
| Keys are strictly typed | Yes | No (any strings) |
| Can be restricted to specific keys | Yes | No |
| Can be combined with specific properties | Harder | Easy |
| Convenient for "dictionary/enum" structures | Yes | Sometimes |
7. A real-life example
A translation dictionary
type Locale = "en" | "fr" | "de";
interface Translations {
[key: string]: Record<Locale, string>;
}
const ui: Translations = {
login: { en: "Login", fr: "Connexion", de: "Anmelden" },
logout: { en: "Logout", fr: "Deconnexion", de: "Abmelden" },
};Summary
In TypeScript, an object with dynamic keys can be described in several ways:
| Scenario | Solution | Example |
|---|---|---|
| Any keys and values | Index signature | [key: string]: string |
| Keys restricted by type | Record<K, T> | `Record<"en" |
| Keys derived from a union type | Mapped Type | [K in Status]: string |
| All fields optional | Partial + Record | Partial<Record<K, T>> |
Short Answer
Interview readyA concise answer to help you respond confidently on this topic during an interview.