Skip to main content

Dynamic object keys

1. Use an index signature

This is the most basic and common way.

javascript
interface Dictionary { [key: string]: string; } const colors: Dictionary = { red: "#ff0000", green: "#00ff00", blue: "#0000ff", };

Here [key: string]: string means: the object can have any number of properties with keys of type string, and all the values must be strings.


Variants of index signatures

SignatureWhat it means
[key: string]: stringkeys are strings, values are strings
[key: number]: booleankeys are numbers, values are booleans
[key: string]: anykeys are any strings, values are of any type
[key: string]: SomeTypevalues match a custom type

An example with a custom type

javascript
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.

javascript
type Dictionary = Record<string, string>; const translations: Dictionary = { hello: "Hola", bye: "Adios", };

Record<K, T> literally means: "an object where each key has type K and the value has type T".


Example: typed keys

javascript
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.

javascript
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:

javascript
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 type string.


5. If you need partial (optional) dynamic keys

You can add Partial<>:

javascript
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

PropertyRecordIndex signature
Keys are strictly typedYesNo (any strings)
Can be restricted to specific keysYesNo
Can be combined with specific propertiesHarderEasy
Convenient for "dictionary/enum" structuresYesSometimes

7. A real-life example

A translation dictionary

javascript
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:

ScenarioSolutionExample
Any keys and valuesIndex signature[key: string]: string
Keys restricted by typeRecord<K, T>`Record<"en"
Keys derived from a union typeMapped Type[K in Status]: string
All fields optionalPartial + RecordPartial<Record<K, T>>

Short Answer

Interview ready
Premium

A concise answer to help you respond confidently on this topic during an interview.