Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Typescript, generic variadic factory function returning tuple

In typescript, a factory function can be created like this, with a return type defined:

function factory1<T>(object: new () => T): T {
  return new object();
}

If I want to create several different objects, I can extend this function to:

function factory2<T, U>(object1: new () => T, object2: new () => U): [T, U] {
  return [new object1(), new object2()];
}

Now to my question: Can this factory pattern be generalized to take any number of unrelated object types and still return a typed tuple? Or is the only option to drop the strong typing and revert to using any?

Something like this:

function factory3<T extends any[]>(...objects: {new(): any}[]): [...T] {
  // BODY
}

The issue with the last one, factory3, is that we lose the type of T and instead get "typeof T". For example:

    factory3(ClassA, ClassB, ClassC);

// actually becomes this:

    factory3<typeof ClassA, typeof ClassB, typeof ClassC>(...) : [typeof ClassA, typeof ClassB, typeof ClassC] {}

// But we want this to happen instead, which is possible for factory 1:

    factory3<typeof ClassA, typeof ClassB, typeof ClassC>(...) : [ClassA, ClassB, ClassC] {}

like image 787
Daniel Avatar asked Mar 21 '26 16:03

Daniel


1 Answers

Sure, you can make the factory rest argument a mapped array/tuple type. You'll need a type assertion or something like it to convince the compiler that the returned value in the body is really a [...T]:

function factory<T extends any[]>(...ctors: { [K in keyof T]: new () => T[K] }) {
    return ctors.map(x => new x) as [...T];
}

You can verify that this works:

class A {
    x = 1;
}
class B {
    u = "u";
}
class C {
    s = true;
}

const objects = factory(A, B, C);
// const objects: [A, B, C]
console.log(JSON.stringify(objects)); // [{"x":1},{"u":"u"},{"s":true}] 

Playground link to code

like image 72
jcalz Avatar answered Mar 24 '26 06:03

jcalz



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!