Can this circular dependency in TypeScript be refactored so separate TypeScript files are possible?

810 views Asked by At

I have those TypeScript classes in the same .ts file because there are import dependencies. I would appreciate if someone could help me refactor the code so I could remove the circular reference of imports:

The first one is the abstract GenericModel :

export abstract class GenericModel {
    nodeClass: string;
    id: string = "0";

    static fromJson(json: any): GenericModel {
        if (json.nodeClass === "Entity") {
            return EntityModel.fromJson(json);
        }
        else if(json.nodeClass === "User") {
            return UserModel.fromJson(json);
        }
        return null;
    }
}

The other two classes are EntityModel(listed below) and UserModel:

export class EntityModel extends GenericModel {
    nodeClass: string = "Entity";
    alias: string;
    description: string;

    constructor(public text: string, public id: string, public uuid: string, alias: string, public rand:string = "") {
       //[...]
    }

    //instance methods
    //[...]

    public static fromJson(json: any): EntityModel {
        var entity = new EntityModel(json.text, json.id, json.uuid, json.alias, json.rand);
        entity.description = json.description;

        if (json.types) {
            for (let type of json.types) {
                let objectifiedType: EntityModel = EntityModel.fromJson(type);
                entity.types.push(objectifiedType);
            }
        }
        if (json.innerEntities){
            for (let innerEntity of json.innerEntities) {
                let objectifiedInnerEntity: EntityModel = EntityModel.fromJson(innerEntity);
                entity.innerEntities.push(innerEntity);
            }
        }
        return entity;
    }
}

What I am doing here is deserialization of JSON using a hierarchy of static calls fromJson() based on nodeClass.

It is obvious that if GenericModel was in a separate file it would need to import EntityModel and UserModel while the other two if they were in separate file they would need to import GenericModel.

GenericModel --- has to import --> EntityModel, UserModel

EntityModel --- has to import --> GenericModel

UserModel --- has to import --> GenericModel

I wonder if there is a way to refactor the code so it does what it does now but also the classes are in separate .ts files.

Thank you!

1

There are 1 answers

4
Andrew Eisenberg On BEST ANSWER

The trick here is to isolate the circular dependencies into their own module. I would extract all the fromJson methods into a single, new module. Then I would convert fromJson into a class called something like ModelFactory, since this is now more similar to a factory pattern. So, the end result would look something like this:

export class ModelFactory {
  // maybe add a constructor for some configuration data, maybe not

  create(json: any) {
    ...
  } 
}

Now, I also see that you type the json object as any. That seems to be bit broad when you seem to know at least a few properties on the type. I'd try to create an interface for the json object, like this:

export interface ModelJson { // find a better name
  text?: string; 
  id?: number;
  uuid?: UUID; 
  alias?: string;
  rand?: number;
  ...
}

type UUID = number;

This is a more typescripty way of doing things.