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)