Reputation: 46479
Assume we have an object like this:
const MyObject = {
gloves: {
gloves1: ...,
gloves2: ...
},
boots: {
boots1: ...,
boots2: ...
}
}
where names like gloves1
are variable. We then create a helper function to get a specific value from MyObject
i.e.
function getValue(category: keyof typeof MyObject, name: string) {
return MyObject[category][name];
}
is there any way to type name
dynamically based on what catefory
was passed as argument? So if I pass gloves
as category name should only be gloves1
or gloves2
Upvotes: 0
Views: 52
Reputation: 327644
Since the example object is invalid I will make up my own, but feel free to replace it:
const MyObject = {
a: { b: "", c: 1, d: true },
e: { f: false, g: 2, h: "x" }
};
type MyObject = typeof MyObject; // for convenience
The right way to do this is to make getValue()
a generic function. Assuming some of the nested properties are of different types, you'd also like getValue()
to return the type corresponding to the particular subproperty. If so, then you want two generic parameters: one corresponding to the category
, and one corresponding to the name
. Here's how you'd type it:
function getValue<K extends keyof MyObject, K2 extends keyof MyObject[K]>(
category: K, name: K2) {
return MyObject[category][name];
}
This compiles now without error. K
is a key of MyObject
. And K2
is constrained to a key of MyObject[K]
, a lookup type corresponding to the property of MyObject
at the K
index.
Let's see if it works:
const good1 = getValue("a", "c"); // number
const good2 = getValue("e", "h"); // string
const bad1 = getValue("a", "h"); // error!
// ----------------------> ~~~
// "h" is not assignable to "b" | "c" | "d"
Looks good. Note how the type of good1
and good2
are different, and correspond to the particular types of MyObject.a.c
and MyObject.e.h
respectively. And you get the desired error with bad1
when you use an invalid subproperty key.
Okay, hope that helps!
Upvotes: 1
Reputation: 10127
Your example object is broken:
const MyObject = {
gloves: {
gloves1: 1,
gloves2: 2
},
boots: {
boots1: 3,
boots2: 4
}
}
With generics:
function getValue<Category extends keyof typeof MyObject>(category: Category, name: keyof typeof MyObject[Category]) {
return MyObject[category][name];
}
getValue("boots", "gloves1"); // Argument of type '"gloves1"' is not assignable to parameter of type '"boots1" | "boots2"'.
Upvotes: 0