Using {[key:string]: string} and {[key:string]: any} in TS

avatar
Borislav Hadzhiev

Last updated: Jan 20, 2023
6 min

banner

# Table of Contents

  1. The {[key: string]: string} type in TypeScript
  2. The {[key: string]: any} type in TypeScript

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

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

The index signature specifies a key and value of type string.

index.ts
// ๐Ÿ‘‡๏ธ function returning index signature // (a key-value structure with key and value strings) function getObj(): { [key: string]: string } { return { name: 'Bobby Hadz', country: 'Chile' }; } // ๐Ÿ‘‡๏ธ Interface using index signature interface Person { [index: string]: string; } // ๐Ÿ‘‡๏ธ const p1: Person const p1: Person = { name: 'Bobby Hadz', country: 'Chile' };

the key string string type

And here is an example of a type using an index signature.

index.ts
// ๐Ÿ‘‡๏ธ Type using index signature type Animal = { [index: string]: string; }; const a1: Animal = { name: 'Alfred', type: 'dog' };

example of using index signature

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

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

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

Let's look at an example of how TypeScript warns us if we try to add a value of a different type than specified in the index signature.

index.ts
interface Person { [index: string]: string; } // โ›”๏ธ ERROR: Type 'number' is not assignable to type 'string'. const p1: Person = { name: 'Bobby', age: 30 };

We've specified that when the object that has a type of Person is indexed with a string, it should return a string.

Trying to set a key that is a string with a value of type number causes an error.

# Overriding the type of a specific property in an index signature

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 doesn't match the type of the string index, so TypeScript errors out.

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: 'Bobby', country: 'Chile', age: 30 };

use union type to resolve the issue

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 that is not in the union causes an error.

index.ts
interface Person { [index: string]: string | number; age: number; name: string; // โ›”๏ธ ERROR: Property 'colors' of type 'string[]' is not assignable // to 'string' index type 'string | number'.ts(2411) colors: string[]; }

# Setting an index signature to readonly

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]: string; } const obj: ReadonlyObj = { name: 'Bobby Hadz', country: 'Chile', }; // โ›”๏ธ Index signature in type 'ReadonlyObj' // only permits reading. obj.name = 'Alfred';

We set the string index's type to readonly, so the type checker errors out if we try to write to a property that has a string key.

# 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: 'Bobby Hadz', age: 30, pizza: true }; } // ๐Ÿ‘‡๏ธ Interface using index signature interface Person { [index: string]: any; } // ๐Ÿ‘‡๏ธ const p1: Person const p1: Person = { name: 'Bobby Hadz', age: 30 };

key string any type in typescript

And here is an example that uses an index signature in a type alias.

index.ts
// ๐Ÿ‘‡๏ธ Type using index signature type Animal = { [index: string]: any; }; const a1: Animal = { name: 'Alfred', age: 3 };

using index signature in type alias

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 the object is indexed with a string, it will return a value of any type.

The index signature {[key: string]: any} is very broad and doesn't 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 a 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: 'Bobby Hadz', 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 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 doesn't 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[]; }

# Narrowing down the type for specific properties

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: 'Bobby Hadz', 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: 'Bobby Hadz', // โ›”๏ธ 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: 'Bobby Hadz', }; // โ›”๏ธ 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 errors out if we try to write to a property that has a string key.

# Additional Resources

You can learn more about the related topics by checking out the following tutorials:

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.

Copyright ยฉ 2023 Borislav Hadzhiev