Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Generic factory parameters in Typescript

This is the sample from the official Typescript documentation for a generic factory. In this sample the constructor does not take parameters.

function create<T>(c: {new(): T; }): T {
  return new c();
}

How could I rewrite this so that in addition to a type, the factory method accepts other parameters and passes them when it invokes the constructor of the class it is instantiating? So the return statement would look something like this:

return c(p1, p2);

Something that wasn't clear to me is that this

{new(): T; }

is effectively an interface, inasmuch as it defines the terms of assessment for compatibility of a proposed class, incidentally also declaring the constructor signature. I'm going to answer my own question.

like image 339
Peter Wone Avatar asked Mar 15 '17 08:03

Peter Wone


People also ask

What is a generic type in TypeScript?

Generics offer a way to create reusable components. Generics provide a way to make components work with any data type and not restrict to one data type. So, components can be called or used with a variety of data types. Generics in TypeScript is almost similar to C# generics.

How do you initialize a generic type in TypeScript?

In C# we can define a generic type argument and instantiate this type at runtime: public class Factory { public T Create<T>() where T: new() { return new T(); } } var factory = new Factory(); var person = factory.

What is a generic type parameter?

Generic Methods A type parameter, also known as a type variable, is an identifier that specifies a generic type name. The type parameters can be used to declare the return type and act as placeholders for the types of the arguments passed to the generic method, which are known as actual type arguments.

Why is generic used in TypeScript?

Generics allow creating 'type variables' which can be used to create classes, functions & type aliases that don't need to explicitly define the types that they use. Generics makes it easier to write reusable code.


2 Answers

This is what I would do:

module Factory {
    export function createInstance<T extends Wibble, K extends IRaw1>(ctor: { new (raw: K): T }, data: K): T {
        return new ctor(data);
    }
}
var raw1 = { Foo: "foo" } as IRaw1;
var d1 = Factory.createInstance(Wibble, raw1);

var raw2 = { Foo: "foo", Bar: "bar" } as IRaw2;
var d2 = Factory.createInstance(Wobble, raw2);

If your constructors need more arguments, then just add them to the one object you're passing to the ctor, this way you won't need to add more generic constraints per argument.

like image 113
Nitzan Tomer Avatar answered Oct 07 '22 03:10

Nitzan Tomer


You can't define a single factory method that can pass an arbitrary number of parameters to the constructor of an arbitrary class.

You can, however, define a factory method for any specific number of constructor parameters. Here's a rather elaborate sample in the Typescript playground. It defines classes and interfaces and derived classes and interfaces and then uses them to demonstrate the factory method working.

Line 38 of the example is probably the most interesting and impressive part of the whole show: an incompatible parameter is specified and this is statically detected by the editor! By implication line 38 has passed the same check, and this matches expectation; Wobble is a derivative of Wibble and therefore acceptable, and the parameter is declared as IRaw2 which is a derivative of the declared parameter type of IRaw1, and therefore also acceptable.

If you wanted a two-parameter constructor it would look like this:

export function createInstance<T1, T2, T3>(ctor: new(p: T1, q: T2) => T3, p:T1, q:T2): T3 {
  return new ctor(p, q);
}
like image 33
Peter Wone Avatar answered Oct 07 '22 05:10

Peter Wone