Reputation: 6069
I dont have much experience with typescript and could do with some help dynamically handling object initialisation:
I have a series of classes which all have a parameters sub class in the following structure:
export class MainClass1 {
getParameters(): MainClass1.Parameters | undefined;
setParameters(value?: MainClass1.Parameters): void;
}
export namespace MainClass1 {
export class Parameters{
}
}
export class MainClass2 {
getParameters(): MainClass2.Parameters | undefined;
setParameters(value?: MainClass2.Parameters): void;
}
export namespace MainClass2 {
export class Parameters{
}
}
The above is code i'm consuming so I cannot edit the class constructor but what I need to do is go from a string. e.g 'MainClass1' to return an instantiated Mainclass1 with an instantiated MainClass1.Parameters object, so right now I have code for each class like the following:
case 'MainClass1':
let mc1 : Lib.MainClass1 = new Lib.MainClass1();
let mc1Params : Lib.MainClass1.Parameters = new Lib.MainClass1.Parameters();
mc1.setParameters(mc1Params);
return mc1;
the return type of the above is an interface like this:
export interface GenericMainClass {
getParameters() : any;
getId(): string;
setId(value: string): void;
getClassName(): string;
getValue(): Lib.Value | undefined;
setValue(value?: Lib.Value): void;
}
As I say what i would ideally like is to be able to handle this instantiation dynamically. Can anyone show me how to instantiate these classes as described from a string name, is this possible and then how I should present a generic wrapper around setParameters, i hoped something like setParameters<T>(value? : T) : void;
on the interface might work, but typescript complains about incompatible types when I try that.
Any help would be appreciated
Upvotes: 0
Views: 86
Reputation: 2574
There is no 100% valid way to handle it in Typescript, but you can try semi-hacky way:
function newClass<TClass extends Lib.GenericMainClass>(name: "Class1" | "Class2"): TClass {
const Class = Lib[name];
const instance = new Lib[name]() as any as TClass;
const parameters = new Lib[name].Parameters();
instance.setParameters(parameters);
return instance;
}
const newInstanceOfClass1 = newClass("Class1");
const newInstanceOfClass2 = newClass("Class2");
const newInstanceOfClass1Typed = newClass<Lib.Class1>("Class1");
const newInstanceOfClass2Typed = newClass<Lib.Class2>("Class2");
Here is url to TypeScript Playground
Explanation:
Lib
is a usual JS object here, and you can still refer to it's keys same as in usual JS.
as any as TClass
is a hack, that forces TypeScript to cast unknown type to what you need to.
<TClass extends ...>
is a Generic type, that used inside the newClass
function and can be defined when you call the newClass
function like this: newClass<MyType>
Without having it defined:
const newInstanceOfClass1 = newClass("Class1");
it will be typeof Lib.GenericMainClass
With having it defined:
const newInstanceOfClass1Typed = newClass<Lib.Class1>("Class1");
it will be typeof Lib.Class1
So you are free to choose, if you need GenericMainClass
or particular class when using newClass
Notes:
If you want to have absolutely any name of the class, then define newClass
function like this:
function newClass<TClass extends Lib.GenericMainClass>(name: string): TClass {
...
If you still want to have control all possible class names, then you can use enum:
enum ClassNames {
Class1: "Class1",
Class2: "Class2",
...
}
function newClass<TClass extends Lib.GenericMainClass>(name: ClassNames): TClass {
...
Upvotes: 1