Beginner's TypeScript #11
Nhan Nguyen
Posted on December 30, 2023
⭐ Assigning Dynamic Keys to an Object ⭐
Consider this createCache function:
const createCache = () => {
const cache = {};
const add = (id: string, value: string) => {
cache[id] = value;
};
const remove = (id: string) => {
delete cache[id];
};
return {
cache,
add,
remove,
};
};
We have an empty object that we are calling cache, and we are allowing users to specify add and remove on it.
There are errors in the createCache function:
Expression of type 'string' can't be used to index type '{}'
There is no indexing happening with our current code..
We will figure out how to reference the TypeScript docs and determine what could be causing this problem.
Update cache to be typed properly so the errors go away.
🌟 Solutions:
1️⃣ Use the Record Utility Type:
One solution is to type cache as a Record:
const cache: Record<string, string> = {}
The first type argument to Record is for the key, and the second is for the value. In our case, both are strings.
The Record type allows us to add any number of dynamic keys to the object at runtime, using something like this:
cache['keyHere'] = 'valueHere'
Record is different than the Set and Map we looked at earlier– it is only at the type level.
2️⃣ Use an Index Signature:
Here is another way to update cache to please TypeScript:
Recall that in the "before" code, we had lots of errors like "You can't use that to index this."
These errors were saying that they could not tell what types the key was. Whenever you see an error about an index, it is usually about an object key!
For this fix, we will add what is called an index signature to our cache:
const cache: {
[id: string]: string;
} = {}
Index signatures put the name of the index and its type (string or number) inside of square brackets.
So from the above, we can see that id is the index for cache.
If we were to set it to [id: number]: string, every time we called cache.add(), we would have to pass a number as the first argument.
3️⃣ Use an Interface with an Index Signature:
We can also create an interface for Cache that contains the index signature:
interface Cache {
[id: string]: string;
}
Then inside of the createCache function we would type cache as Cache:
const createCache = () => {
const cache: Cache = {};
This would work with a type instead of interface as well.
Which One to Use? 🧐
All of these solutions are fine-- there are no pros or cons between them.
Assigning dynamic keys to an object is a common pattern in JavaScript, and these techniques allow you to do it.
I hope you found it useful. Thanks for reading. 🙏
Let's get connected! You can find me on:
- Medium: https://medium.com/@nhannguyendevjs/
- Dev: https://dev.to/nhannguyendevjs/
- Hashnode: https://nhannguyen.hashnode.dev/
- Linkedin: https://www.linkedin.com/in/nhannguyendevjs/
- X (formerly Twitter): https://twitter.com/nhannguyendevjs/
Posted on December 30, 2023
Join Our Newsletter. No Spam, Only the good stuff.
Sign up to receive the latest update from our blog.