I have an object and I want to enforce it to contain all keys of an Enum, and I also want the type of it’s values to be inferred. So if I do this:
enum RequiredKeys {
A = 'a',
B = 'b'
}
const objectThatShouldContainAllRequiredKeys = {
[RequiredKeys.A]: (id: string) => {}
};
// Argument of type '123' is not assignable to parameter of type 'string'
// Which is great, that's exactly what I want.
objectThatShouldContainAllRequiredKeys[RequiredKeys.A](123);
But now, I tried enforcing the object keys and every solution I try breaks the type inference. For example:
enum RequiredKeys {
A = 'a',
B = 'b'
}
// Property 'b' is missing in type '{ a: (id: string) => void; }' but required in type 'Record<RequiredKeys, Function>'.
// Which is great, that's exactly what I want
const objectThatShouldContainAllRequiredKeys: Record<RequiredKeys, Function> = {
[RequiredKeys.A]: (id: string) => {}
};
// No error here, which is less great...
objectThatShouldContainAllRequiredKeys[RequiredKeys.A](123);
Any idea how I can enjoy both worlds? Have the object enforce all keys from the enum and infer the object values? Thanks!!
You can create identity function with type parameter constrained to have required keys, so typescript will validate the passed object keys and will infer its values' types:
const createWithRequiredKeys = <T extends Record<RequiredKeys, unknown>>(obj: T) => obj;
const withRequiredKeys = createWithRequiredKeys({
[RequiredKeys.A]: (id: string) => {},
[RequiredKeys.B]: 'foo',
});
// withRequiredKeys is { a: (id: string) => void; b: string; }
withRequiredKeys[RequiredKeys.A](123); // 'number' is not assignable to parameter of type 'string'