Index Accessed Types page
Learn how to look up types of properties with index accessed types!
Overview
There are many times we would like to forego defining a type and rather use some other type as a reference instead. Index Accessed Types is a TypeScript feature that allows us to do just that! In this section, we will look at index accessed types, how they work, and how they are defined. Towards the end we will see a practical application!
Index Accessed Types
Index accessed types are a way to look up types on other types. TypeScript uses square bracket notation ([]
) to grab the desired type off of object types, much like how you can access a specific property from a JavaScript object using square brackets (someObject["someKey"]
).
In the example above 'name'
, 'power'
, and 'type'
are not strings, but types (literal types). Index access types can only be used with types. Any value provided will produce an error.
If the type provided is not able to index a particular type, TypeScript will provide an error.
Multiple Types as Indices
When TypeScript sees more than one type being passed in it will create a union of the property types — keyof
and string unions illustrate this.
It might be surprising to see that
Pokemon[keyof Pokemon]
isstring | number
and notstring | number | "fire" | "water" | "grass"
. This happens because the value constraints of the string literal union do not have any impact on the newly formed type. The type"fire" | "water" | "grass"
is a subset ofstring
meaning anystring
provided tostring | number | "fire" | "water" | "grass"
will pass. TypeScript knows this and purposely omits the union for clarity.
Index access types are chainable, this allows us to access as deep a property as we would like simply by adding more brackets.
With Generics
Index accessed types also work with generics, let’s take a look at an example they may seem familiar if you did the exercises from the last section
In this exercise we didn’t define the return type of this function; instead opting for TypeScript’s type inference to define it for us. If we were to define it the return type would be Starters[Name]
. The reason this works is that we have constrained the Name
generic to extend keyof Starters
.
Practical Indices
Index accessed types really shine when deriving types from other types. Imagine we’re building a game that uses some sort of restful service. The backend has done a wonderful job of building a consistent API that describes all entities in the following way.
Action | HTTP Method | Path |
Get all | GET |
/<entity> |
Get one | GET |
/<entity>/:id |
Create | POST |
/<entity> |
Update | PATCH |
/<entity>/:id |
Delete | DELETE |
/<entity>/:id |
Ideally, we’d like a generic factory to create requests for multiple crud-ish entities, like a HatchedPokemon
or a Trainer
, to reduce the duplicated logic in our code. One way we could accomplish this is by constraining the type of entity passed into our factory to some base Entity
type that describes what all entities must have, in our case, this is an id
value, and then having all the methods returned by the factory use the type of that Entity
’s id.
This works, but what would happen if, down the long development road, id
is changed from a number
to a string
to conform to the UUIDv4
standard? We’d have to change it on Entity
and then on all of our request function types as well. It would be much easier if we just looked up the type of id
off of the generic passed into the Request
type.
Note: Notice this is only possible because we have constrained
T
to extendEntity
in the generic definition
Using index accessed types we have resolved this issue and any future issue like it down the road.
As we’ve seen, indexed access types are a powerful feature from TypeScript which helps our typing become more flexible and dynamic. Like generics, they will become a staple in our types as we progress through other TypeScript features.
Exercises
Exercise 1
Below is an array of Pokemon trainers and a FavoritePokemon
type currently assigned to any.
Using the pokemonTrainers
array replace the any
with a type that has the same shape as favoritePokemon
on the
trainers in the array.