Daniel
Daniel

Reputation: 1175

Typescript generic factory function including parameters for created object

I am trying to create a factory class in Typescript. The factory should be set up to create a specific class of object, including default parameters set up at factory initialization.

Here's what I've come up with so far:

type Constructor<T> = new(...args: any) => T;

class Factory<T> {
    private params: ConstructorParameters<Constructor<T>>;
    private ctor: Constructor<T>;
    init<TCtor extends Constructor<T>>(tConstructor: TCtor,
        ...params: ConstructorParameters<TCtor>)
    {
        this.ctor = tConstructor;
        this.params = params;
    }

    create(): T {
        return new this.ctor(...this.params);
    }
}

class ClassWithParams { constructor(a: number) { } }

const factory = new Factory<ClassWithParams>();
//factory.init(ClassWithParams);
// Compilation error - missing param a

factory.init(ClassWithParams, 1);
console.log(factory.create());
// instance of ClassWithParams

The above code works well (Typescript playground). However, it requires an init() call for the factory directly after construction. I can't move the init() code into the constructor because init() relies on a generic parameter TCtor in order to properly typecheck the parameter list.

I could make TCtor a generic parameter of the class instead, but that would mean every time Factory is referenced generically an additional type parameter would need to be supplied for no reason.

Any ideas to remove that init() call?

Upvotes: 0

Views: 1736

Answers (1)

Aluan Haddad
Aluan Haddad

Reputation: 31803

What's wrong with the following code

type Constructor<T> = new (...args: any[]) => T;

class Factory<T, C extends Constructor<T> = Constructor<T>> {
    params: ConstructorParameters<C>;
    ctor: Constructor<T>;
    constructor(tConstructor: Constructor<T>,
        ...params: ConstructorParameters<C>) {
        this.ctor = tConstructor;
        this.params = params;
    }

    create(): T {
        return new (this.ctor)(...this.params)
    }
}
class ClassWithParams { constructor(a: number) { this.a = a; } a: number } 

const factory1 = new Factory(ClassWithParams);

const factory2 = new Factory(ClassWithParams, '1');

const factory3 = new Factory(ClassWithParams, 0);


const value = new Factory(ClassWithParams, 0).create().a;

As seen in this playground it's convenient and correct.

Upvotes: 1

Related Questions