I have a unique object that I need to transform into various structures based on its keys. Each key-value pair must be treated individually, so I intend to convert the object into an array of entries, then map those entries into policy objects and finally execute them all. My objective is to utilize this object in the following manner:
type ValuesMap = {key1: Value1; key2: Value2; ...};
const valuesMap: ValuesMap = getValues();
const results = Object.entries(valuesMap).map(([key, value]) => policyFactory.createPolicy(key, value).apply());
To make this possible, I plan to establish an abstract policy class and a factory for these policies:
type Key = keyof ValuesMap;
type Value<K extends Key> = ValuesMap[K];
abstract class Policy<K extends Key> {
constructor(protected readonly value: Value<K>) {}
abstract apply();
}
class ConcreteKey1Policy extends Policy<'key1'> {
//This class's constructor should only accept a Value1 argument; TypeScript should raise an error if any other type is specified
apply() {/*...*/}
}
//...
class PolicyFactory {
private readonly constructorMap: {
[K in Key]: new (value: Value<K>) => Policy<K>;
} = {
key1: ConcreteKey1Policy;
key2: ConcreteKey2Policy;
//Repeat for all relevant keys in ValuesMap
};
createPolicy<K extends Key>(type: K, value: Value<K>): Policy<K> {
return new this.constructorMap[type](value); //error1
switch(type) {
case 'key1':
return new ConcreteKey1Policy(value); //error2
//...
}
}
}
I have tried two approaches for the factory class, one using a map of possible keys and corresponding constructors, and the other employing a switch for a type
parameter.
Both strategies result in TypeScript errors:
- When attempting to create an object from the constructor map, the error "type '"key1"' is not assignable to type 'K'. '"key1"' is compatible with the constraint of type 'K', but 'K' could potentially be instantiated with a different subtype of the 'Key' constraint."
- When utilizing the switch statement, every creation of a concrete policy triggers the error "Argument of type 'Value1 | Value2 | Value3 | Value4' cannot be assigned to parameter of type 'Key1'. Type 'Value2' is not compatible with type 'Value1'."
I comprehend the nature of both errors, however, resolving them poses a challenge. I aim to incorporate generics in the factory where the type of value
aligns with the type
, yet by invoking extends Key
, a constraint encompasses any subtype of Key
. What I truly require is any type within the union of Key
without exceptions. How can such a constraint be established? Is there a simpler method to achieve the desired outcome?