TypeScript Object with Dynamic Keys
When working with TypeScript, there might be scenarios where you do not have predetermined keys for an object. These dynamic keys can come from user input, external data, or other runtime sources. In this guide, we will explore different ways to define, create, and work with objects having dynamic keys in TypeScript.
Basic Definition
For objects with dynamic keys, TypeScript provides the Record
type and index signatures.
Index Signature
You can use an index signature to specify that an object could have any number of properties of a certain type.
interface StringDictionary { [key: string]: string; } const obj: StringDictionary = { key1: "value1", key2: "value2", };
In this example, the keys are strings, and the values are also strings. You can change the value type as per your requirements.
Using the Record
type
The Record
type allows you to create an object type where you specify the key type and value type.
const obj: Record<string, string> = { key1: "value1", key2: "value2", };
Using Enums as Keys
If you have a limited set of dynamic keys, enums
can be a handy choice.
enum Keys { FIRST = "firstKey", SECOND = "secondKey", } type EnumDictionary = Record<Keys, string>; const obj: EnumDictionary = { [Keys.FIRST]: "value1", [Keys.SECOND]: "value2", };
Combining Static and Dynamic Keys
In real-world scenarios, you might have objects with both static and dynamic keys.
interface MixedDictionary { staticKey: string; [key: string]: string | number; // Can accommodate the staticKey too } const obj: MixedDictionary = { staticKey: "staticValue", dynamicKey1: "value1", dynamicKey2: "value2", };
Nested Dynamic Keys
Objects can have nested structures with dynamic keys at various levels.
interface NestedDictionary { [key: string]: { innerKey: string; [nestedKey: string]: string | number; }; } const obj: NestedDictionary = { outerKey1: { innerKey: "value", nestedKey1: "nestedValue1", }, outerKey2: { innerKey: "value", nestedKey2: 42, }, };
Constrained Dynamic Keys
At times, you'd want to constrain the dynamic keys to a specific set of values, perhaps based on another array or tuple.
const validKeys = ['a', 'b', 'c'] as const; type ValidKeysType = typeof validKeys[number]; type ConstrainedDictionary = Record<ValidKeysType, string>; const obj: ConstrainedDictionary = { a: "valueA", b: "valueB", c: "valueC", };
Here, obj
can only have keys 'a', 'b', or 'c'.
Handling Unknown Keys
Sometimes, you might be unsure about the object's shape. TypeScript provides an unknown
type for such cases.
type UnknownKeyDictionary = { [key: string]: unknown; }; const obj: UnknownKeyDictionary = { key1: "value1", key2: 42, key3: { inner: "value" }, };
Using unknown
forces you to type-check values before operating on them, enhancing type safety.
Using with Functions
When dealing with functions that operate on objects with dynamic keys, you can utilize generics for better type inference.
function getValue<T, K extends keyof T>(obj: T, key: K): T[K] { return obj[key]; } const obj = { key1: "value1", key2: 42, }; const result1 = getValue(obj, 'key1'); // result1 inferred as string const result2 = getValue(obj, 'key2'); // result2 inferred as number
Remember, while dynamic keys provide flexibility, they can also introduce challenges in ensuring type safety. Always strive for the right balance between flexibility and safety when modeling your types.
Invite only
We're building the next generation of data visualization.
How to turn webpages into editable canvases with a JavaScript bookmarklet
Kris Lachance
How to fix the "not all code paths return a value" issue in TypeScript
Kris Lachance
Working with WebSockets in Node.js using TypeScript
Kris Lachance
Type Annotations Can Only Be Used in TypeScript Files
Kris Lachance
Guide to TypeScript Recursive Type
Kris Lachance
How to Configure Knex.js with TypeScript
Kris Lachance