Dynamically add Properties to an Object in TypeScript

avatar

Borislav Hadzhiev

Thu Feb 24 20223 min read

banner

Photo by Robson Morgan

Dynamically add Properties to an Object in TypeScript #

Use an index signature to dynamically add properties to an object, e.g. const obj: {[key: string]: any} = {}. Index signatures are used when we don't know all of the names of a type's properties and the type of their values ahead of time.

index.ts
interface Person { [key: string]: any; } const obj: Person = {}; obj.name = 'Tom'; obj.age = 30;

The {[key: string]: any} syntax is an index signature in TypeScript and is used when we don't know all the names of a type's properties and the shape of the values ahead of time.

The index signature in the examples means that when an the object is indexed with a string, it will return a value of any type.

You might also see the index signature {[key: string]: string} in examples. It represents a key-value structure that when indexed with a string returns a value of type string.

If the value of the string keys were set to any in the index signature, you could add properties to the object of any type, since anything is more specific than any.

index.ts
interface Person { [key: string]: any; age: number; name: string; country?: string; } const obj: Person = { name: 'Tom', age: 30, }; obj.country = 'Chile'; obj.language = 'TypeScript';

This is a good way to narrow down the type of some of the properties that you know ahead of time.

For example, if I try to set the age property to a string, the type checker would throw an error because it expects a number.

index.ts
interface Person { [key: string]: any; age: number; name: string; country?: string; } const obj: Person = { name: 'Tom', age: 30, }; // ⛔️ Error: Type 'string' is not // assignable to type 'number'.ts(2322) obj.age = '100';

We've already set the age property to have a type of number, so the type checker helps us spot an error in our application.

A newer way to achieve this is to use the Record utility type.

Use the Record utility type to dynamically add properties to an object, e.g. const obj: Record<string, any>. The Record utility type constructs an object type, whose keys and values are of specific type.

index.ts
const obj: Record<string, any> = { name: 'Tom', }; obj.age = 30; obj.country = 'Chile';

We typed the object above to have keys of type string and values of type any.

The first type we passed to the generic is the type for the Keys, and the second - the type for the values.

If you know the type of some of the values ahead of time, you can specify them in an interface for better type safety.

index.ts
interface EmployeeData extends Record<string, any> { role: string; salary: number; color?: string; } const employees: Record<string, EmployeeData> = { tom: { role: 'accountant', salary: 10, color: 'blue' }, alice: { role: 'manager', salary: 15 }, }; // ⛔️ Type 'number' is not // assignable to type 'string'.ts(2322) employees.tom.role = 100; // ✅ still works employees.tom.hello = 'world';

The interface EmployeeData extends from the Record constructed type with string keys and any type values.

The interface sets a specific type for the 3 properties that we know about in advance.

If we try to set the role, salary or color properties to an incompatible type, we'd get an error.

Use the search field on my Home Page to filter through my more than 1,000 articles.