When the object properties are not known ahead of time but only the object's key
type and value
type (aka object structure), we can make use of index signatures
in TypeScript.
TL;DR
// Index signature
// where `key` type is `string`
// and `value` type is `number`
interface Details {
[key: string]: number;
}
// a simple function that gets
// some details of the user
function getDetails(details: Details) {
console.log(details);
}
// call the function
getDetails({ salary: 200000, age: 23 }); // ✅ Valid.
getDetails({ age: 23 }); // ✅ Valid.
getDetails({ name: "John Doe" }); // ❌ Error. Type 'string' is not assignable to type 'number'.
To understand what index signatures is, let's consider an example of a function that get some details from the user through an object
parameter like this,
// a simple function that gets
// some details of the user
function getDetails(details) {
console.log(details);
}
Here we have a problem where we do not know the correct property names and the types the details
object should hold. So we cannot create a normal type using an interface
or type alias
.
But the only information we have is that every key
in the details
object will be a string
type and the value
will be a number
type. So with this information, we can create something called an index signature
in TypeScript.
To create an index signature
,
- Firstly, we can either use an
interface
ortype alias
declaration or even an anonymous type declaration. - Then inside the declaration body, we can write a square bracket symbol (
[]
) - Inside those square brackets let's write a variable name to denote the
key
(let's write it askey
only!) followed by thecolon
symbol (:
) then the write thetype
everykey
in the object should hold. In our case, it is thestring
type. - After the brackets, we can add a
colon
symbol (:
) and then write out the type for property'svalue
should hold. In our case, it is thenumber
type.
It may look like this,
// Index signature
// where `key` type is `string`
// and `value` type is `number`
interface Details {
[key: string]: number;
}
Now that we have successfully created an index signature
. We can add this as the type for the details
parameter in the getDetails
function.
It can be done like this,
// Index signature
// where `key` type is `string`
// and `value` type is `number`
interface Details {
[key: string]: number;
}
// a simple function that gets
// some details of the user
function getDetails(details: Details) {
console.log(details);
}
Let's now call the function with different objects structures and see what happens,
// Index signature
// where `key` type is `string`
// and `value` type is `number`
interface Details {
[key: string]: number;
}
// a simple function that gets
// some details of the user
function getDetails(details: Details) {
console.log(details);
}
// call the function
getDetails({ salary: 200000, age: 23 }); // ✅ Valid.
getDetails({ age: 23 }); // ✅ Valid.
getDetails({ name: "John Doe" }); // ❌ Error. Type 'string' is not assignable to type 'number'.
As you can see from the above code the first 2 function calls are valid because the object property's key
is of string
type and the value
is of number
type which satisfies the index signature
but the third function call is invalid since the name
property's value
type is of string
type which is not according to the index signature
.
So index signature
helps in enforcing the type for every key
and value
in an object. This is useful when we do not know the property names and their corresponding types for an object ahead of time and thus it still helps in having a type check.
See the above code live in codesandbox.
That's all 😃!