What does {[key: string]: any} mean in TypeScript

avatar

Borislav Hadzhiev

Last updated: Feb 18, 2022

banner

Photo from Unsplash

The {[key: string]: any} type in TypeScript #

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 their values ahead of time. The index signature specifies that when an object is indexed with a string, it returns a value with any type.

index.ts
// 👇️ function returning index signature // (a key-value structure with key string and value any) function getObj(): { [key: string]: any } { return { name: 'Tom', age: 30, pizza: true }; } // 👇️ Interface using index signature interface Person { [index: string]: any; } // 👇️ const p1: Person const p1: Person = { name: 'Tom', age: 30 }; // 👇️ Type using index signature type Animal = { [index: string]: any; }; const a1: Animal = { name: 'Alfred', age: 3 };

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.

The index signature {[key: string]: any} is very broad and does not really illustrate the purpose of index signatures.

Let's look at an example of a more narrow index signature and how trying to set a value of different type causes the type checker to error out.

index.ts
interface Person { [index: string]: string; } // ⛔️ Error: Type 'number' is not assignable to type 'string'. const p1: Person = { name: 'Tom', age: 30 };

The index signature in the example means that when an object is indexed with a string, it returns a value of type string.

This is why we get an error when trying to add a property to the object with type string that has a value of type number.

You might try to override the type of a specific property with an index signature.

index.ts
interface Person { [index: string]: string; // ⛔️ Error: Property 'age' of type 'number' // is not assignable to 'string' index type 'string'. age: number; }

But the type of the age property in the example does not match the type of the string index, so TypeScript gives us an error.

In this situation, you can set the type of the string index to a union.

index.ts
interface Person { [index: string]: string | number; age: number; name: string; } // 👇️ const p1: Person const p1: Person = { name: 'Tom', age: 30, country: 'Chile' };

The type of the values with an index signature of type string is a union of string and number.

The age and name properties have a value that is a subtype of the union, so we don't get an error.

Trying to add a property to the type that is not in the union, would cause an error.

index.ts
interface Person { [index: string]: string | number; age: number; name: string; // ⛔️ Error: Property 'languages' of type 'string[]' is // not assignable to 'string' index type 'string | number'. languages: 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 { [index: string]: any; age: number; name: string; languages: string[]; } // 👇️ const p1: Person const p1: Person = { name: 'Tom', age: 30, country: 'Chile', languages: ['english', 'spanish'], };

This would be 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 add an age property to the object that has a type of string, the type checker would throw an error, because it expects a number.

index.ts
interface Person { [index: string]: any; age: number; name: string; languages: string[]; } // 👇️ const p1: Person const p1: Person = { name: 'Tom', // ⛔️ Error: Type 'string' is not // assignable to type 'number'. age: 'twenty', country: 'Chile', languages: ['english', 'spanish'], };

You can also set an index signature to readonly if you need to prevent assignment to their indices.

index.ts
interface ReadonlyObj { readonly [index: string]: any; } // 👇️ const p1: Person const p1: ReadonlyObj = { name: 'Tom', }; // ⛔️ Error: Index signature in type 'ReadonlyObj' // only permits reading. p1.name = 'James';

We set the string index's type to readonly, so the type checker gives us an error if we try to write to a property that has a string key.

I wrote a book in which I share everything I know about how to become a better, more efficient programmer.
book cover
You can use the search field on my Home Page to filter through all of my articles.