I want to write a function that create instances of object. I know that the built objects will have just one field, but both the name of the property and the type of the value associated to it should be set "dynamically" (it's enough if I can specify the property name using constants).
I tried to play with generics and restrictions but I don't really understand them.
// the goal: create an object with a property that is an 1-element array
function factory<TContainer, TElement>(key: string, element: TElement): TContainer {
return {[key]: [element]};
}
// how I want to use it
// given a type
type T1 = {
key1: number[];
};
// instead of
const e1a: T1 = { key1: [1]};
const e1b: T1 = { key1: [1, 2]};
const e1c: T1 = { key1: []};
// I want to write
const e1z: T1 = factory<T1, number>('key1', 999); // e1z === {key1: [999]};
const e2z: T2 = factory<T2, string>('key2', 'z'); // e2z === {key2: ['z']};
// moreover I want also to use it without explicit types
const onthefly:{onthefly:string[]} = factory<{onthefly:string[]}, string>('onthefly', 'onthefly'); // onthefly === {onthefly: ['onthefly']};
// even better if the compiler knew what the types will be without telling him
const magic = factory('magic', 3); // magic:{magic:number[]} === {magic:[3]}
but all I get are errors like
Type '{ [x: string]: TElement[]; }' is not assignable to type 'TContainer'.
'TContainer' could be instantiated with an arbitrary type which could be unrelated to '{ [x: string]: TElement[]; }'.
CodePudding user response:
You can rewrite the factory
function like this:
function factory<K extends string, V>(key: K, element: V) {
return { [key]: [element] } as { [Key in K]: [V] };
}
Now we actually use both generic types as arguments so they can be properly inferred. K
holds the key of the resulting object while V
holds the value type.
Calling the function will result in the following types:
const e1z = factory('key1', 999); // { key1: [number] };
const e2z = factory('key2', 'z'); // { key2: [string] };
const onthefly = factory('onthefly', 'onthefly'); // { onthefly: [string] };
const magic = factory('magic', 3); // { magic: [number] }
You may notice here that the types are widened. So 999
becomes just number
. Let me know if this is ok for you. There are more complicated solutions which will narrow down the type further.