Reputation: 21030
I have the following predefined object:
export const catalog = {
category1: {
newDomain: 'Hello'
},
category2: {
otherStuff: 30
}
};
Now I need to achieve two things:
newDomain
and otherStuff
catalog
object) and then second argument as the valid key under the selected category id and finally, return the value of the selected key.It means I need a function validate
such that:
const value1: string = validate('category1', 'newDomain');
const value2: number = validate('category2', 'otherStuff');
Any other combination for validate function should fail.
Upvotes: 0
Views: 189
Reputation: 33931
Edit: I forgot to answer the first part (getting the union of nested keys):
type Values<T> = T[keyof T];
type NestedKey<T extends Record<PropertyKey, Record<PropertyKey, unknown>>> =
Values<T> extends infer T1 ? (T1 extends T1 ? keyof T1 : never) : never;
const catalog = {
category1: { newDomain: 'Hello' },
category2: { otherStuff: 30 },
};
type NestedCatalogKey = NestedKey<typeof catalog>; // "newDomain" | "otherStuff"
First, write a function to do it for any object that has object values, then curry it:
function validate <
R,
K0 extends PropertyKey,
K1 extends PropertyKey,
T extends Record<K0, Record<K1, R>>,
>(l0Key: K0, l1Key: K1, obj: T): R {
return obj[l0Key][l1Key];
}
const catalog = {
category1: { newDomain: 'Hello' },
category2: { otherStuff: 30 },
};
function validateCatalog <
K0 extends keyof typeof catalog,
K1 extends keyof typeof catalog[K0],
>(l0Key: K0, l1Key: K1): typeof catalog[K0][K1] {
return validate(l0Key, l1Key, catalog);
}
const value1 = validateCatalog('category1', 'newDomain'); // string
const value2 = validateCatalog('category2', 'otherStuff'); // number
const value3 = validateCatalog('category1', 'otherStuff'); /*
~~~~~~~~~~~~
Argument of type '"otherStuff"' is not assignable to parameter of type '"newDomain"'.(2345) */
const value4 = validateCatalog('category2', 'newDomain'); /*
~~~~~~~~~~~
Argument of type '"newDomain"' is not assignable to parameter of type '"otherStuff"'.(2345) */
console.log({ value1, value2 }); // { value1: "Hello", value2: 30 }
Upvotes: 1