Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Create a generic factory in typescript

I am writing a small model system for my current project. I would like the consumer of the library to be able to provide their own model definition to the API. The API should output instances of the user's model when querying the server.

// Library Code
interface InstanceConstructor<T extends BaseModel> {
    new(): T;
}

class Factory<T extends BaseModel> {
    constructor(private cls: InstanceConstructor<T>) {}

    get() {
        return new this.cls();
    }
}

class BaseModel {
    refresh() {
        // Refresh returns a new instance, but it should be of 
        // type Model, not BaseModel.
    }
}

// User Code
class Model extends BaseModel {
    // Custom Model
    do() {
        return true;
    }
}

I can't figure out how to finish the pattern here. Just getting the factory to spit out the right instances is pretty easy, however things like clone/refresh on the BaseModel need to also return Model, and not any.

Updated 10/2

After trying typescript@next (technically 1.8-dev at this moment) I seem to be able to get around the issue where the model can reference itself (this) and the type system can follow it. However, I'm unable to

// Library Code
export interface InstanceConstructor<T extends BaseModel> {
    new(fac: Factory<T>): T;
}

export class Factory<T extends BaseModel> {
    constructor(private cls: InstanceConstructor<T>) {}

    get() {
        return new this.cls(this);
    }
}

export class BaseModel {
    constructor(private fac: Factory<this>) {}

    refresh() {
        // get returns a new instance, but it should be of
        // type Model, not BaseModel.
        return this.fac.get();
    }
}

// User Code, Custom Model
export class Model extends BaseModel {
    do() {
        return true;
    }
}

// Kinda sucks that Factory cannot infer the "Model" type
let f = new Factory<Model>(Model);
let a = f.get();

let b = a.refresh();

I've opened an issue on the typescript tracker here: https://github.com/Microsoft/TypeScript/issues/5493

Updated 12/1 (Unsolved)

This, according to the typescript issue tracker, is not possible. The "Polymorphic this" feature only works for non-static class members which would exclude the constructor.

like image 815
Xealot Avatar asked Oct 19 '22 23:10

Xealot


1 Answers

You'll need to use the special this type:

class BaseModel {
    refresh(): this {
        // Refresh returns a new instance, but it should be of 
        // type Model, not BaseModel.
    }
}

At time of writing, this feature is only available in nightly builds of TypeScript (npm install typescript@next), and will be available in TypeScript 1.7. See https://github.com/Microsoft/TypeScript/pull/4910 if you want to track the specific commit or read more about how this works

like image 58
Ryan Cavanaugh Avatar answered Oct 21 '22 17:10

Ryan Cavanaugh