Reputation: 33
I have a nested object by keys that looks like the following:
const OBJECTS = {
OBJECT1: {
properties: {
prop1: { value: "a" },
},
},
OBJECT2: {
properties: {
prop2: { value: "b" },
},
},
} as const;
I'm trying to create a generic type where I an extract out the property values for a specific object - something like:
type PropertiesForObject<OBJECT_KEY extends keyof typeof OBJECTS> = {
[PROPERTY_NAME in keyof typeof OBJECTS[OBJECT_KEY]["properties"]]: typeof OBJECTS[OBJECT_KEY]["properties"][PROPERTY_NAME]["value"];
};
However, I'm getting the following error:
Type '"value"' cannot be used to index type '{ readonly OBJECT1: { readonly properties: { readonly prop1: { readonly value: "a"; }; }; }; readonly OBJECT2: { readonly properties: { readonly prop2: { readonly value: "b"; }; }; }; }[OBJECT_KEY]["properties"][PROPERTY_NAME]'.
Is there any way to index into each object's properties, given that the shape of the properties is the same? I could force it into an interface, but I want to keep the strong typings of the values.
Upvotes: 2
Views: 5166
Reputation: 328262
This might well be a bug or design limitation in TypeScript; the compiler doesn't recognize that the generic object will have a literal key. It's a problem with the inferred constraint on typeof OBJECTS[K]["properties"][P]
. See microsoft/TypeScript#21760 for more information (and maybe give it a 👍 if you want to see it handled someday, but it doesn't look promising since it's been lying fallow for more than two years as of May 2020).
One way to handle this is to make the type function more general (instead of typeof OBJECTS
, use a generic type T
that is explicitly constrained to something with the structure we're accessing) and then specialize that to typeof OBJECTS
:
type GenericPropertiesForObject<
T extends Record<K, { properties: { [k: string]: { value: any } } }>,
K extends PropertyKey
> = {
[P in keyof T[K]["properties"]]: T[K]["properties"][P]["value"]
};
type PropertiesForObject<K extends keyof typeof OBJECTS> =
GenericPropertiesForObject<typeof OBJECTS, K>;
This compiles without error, even though it's performing essentially the same computation. Let's make sure it works:
type O1Props = PropertiesForObject<'OBJECT1'>;
// type O1Props = { readonly prop1: "a"; }
type O2Props = PropertiesForObject<'OBJECT2'>;
// type O2Props = { readonly prop2: "b"; }
Looks good to me. Okay, hope that helps; good luck!
Upvotes: 6