What's the Problem?
Let's say you have a big TypeScript object. It has objects inside objects. You want to get all the keys, even the nested ones. But TypeScript doesn't make this easy.
Look at this User object:
type User = {
id: string;
name: string;
address: {
street: string;
city: string;
};
};
You want "id", "name", and "address.street". But TypeScript just shrugs. The usual way? It's useless:
type UserKeys = keyof User;
This only gives you the top-level keys. It misses the nested goodies like "address.street".
So, we need to get clever. We'll use some TypeScript magic:
- Conditional Types (if-then for types)
- Mapped Types (change each part of a type)
- Template Literal Types (make new string types)
- Recursive Types (types that refer to themselves)
Don't panic. It's not as scary as it sounds.
Here's our solution:
type ExtractKeys<T> = T extends object
? {
[K in keyof T & string]:
| K
| (T[K] extends object ? `${K}.${ExtractKeys<T[K]>}` : K);
}[keyof T & string]
: never;
Yes, it looks like a cat walked on your keyboard. But it works. Let's break it down:
- We check if T is an object.
- If it is, we look at each key.
- For each key, we either keep it as-is or...
- If the key's value is another object, we add the key, a dot, and all the keys inside it.
- We do this for all keys.
Now let's use it:
type UserKeys = ExtractKeys<User>;
Voila! We've got all the keys, even the nested ones.
Why bother? It makes your code safer. Look:
const user: User = {
id: "123",
name: "John Doe",
address: {
street: "Main St",
city: "Berlin",
},
};
function getProperty(obj: User, key: UserKeys) {
const keys = key.split(".");
let result: any = obj;
for (const k of keys) {
result = result[k];
}
return result;
}
// This works
getProperty(user, "address.street");
// This gives an error
getProperty(user, "address.country");
TypeScript catches your mistakes before they blow up in your face.
Remember
- This type can be slow for very big objects.
- It doesn't change how your code runs. It only helps catch errors early.
- It can make your code harder to read. Use it wisely.
Wrap-Up
We've learned to wrangle all the keys from nested TypeScript objects. It's like having x-ray vision for your data. But remember, with great power comes great responsibility. And slower compile times.
Top comments (0)