0

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 Answer 1

2

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.

Sign up to request clarification or add additional context in comments.

4 Comments

Thanks! I had the feeling I would need to add a new class like the factory you suggested! Thanks for the tip of the interface/json :) Btw what is type UUID = number; at the end? Happy New Year!
That is just a type alias. When you have something like a UID that happens to be a number, but really it's just a technical detail and it has its own semantics, you often use a type alias. This ensures that you can only use UIDs where expected and you won't accidentally pass in a vanilla number.
for me the uuid is a string though! Can I still do: type UUID = string;? Thanks
Of course. More information about type aliases are found here: typescriptlang.org/docs/handbook/advanced-types.html In general, I mostly use type aliases for better documentation of parameters and properties. They are never fundamental to the application.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.