Last updated: Feb 27, 2024
Reading timeยท6 min
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
.
// ๐๏ธ 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' };
Here is an example of a type using an index signature.
// ๐๏ธ Type using index signature type Animal = { [index: string]: string; }; const a1: Animal = { name: 'Alfred', type: 'dog' };
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.
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.
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.
You might try to override the type of a specific property with an index signature.
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.
interface Person { [index: string]: string | number; age: number; name: string; } // ๐๏ธ const p1: Person const p1: Person = { name: 'Bobby', country: 'Chile', age: 30 };
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.
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[]; }
You can also set an index signature to readonly
if you need to prevent
assignment to their indices.
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}
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.
// ๐๏ธ 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 };
Here is an example that uses an index signature in a type alias.
// ๐๏ธ 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.
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.
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
.
number
.You might try to override the type of a specific property with an index signature.
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.
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.
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
.
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 an object that has a type of
string
, the type checker would throw an error because it expects a number.
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.
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.
You can learn more about the related topics by checking out the following tutorials: