Is there a way to refactor this circular dependency in TypeScript to enable separate TypeScript files?

I have grouped my TypeScript classes in the same .ts file due to import dependencies. I am seeking assistance to refactor the code and eliminate the circular reference of imports:

First, let's consider the abstract class 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 next two classes are EntityModel (shown 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;
    }
}

I am currently performing JSON deserialization using a hierarchy of static calls fromJson() based on the nodeClass.

If the GenericModel were in a separate file, it would require imports from EntityModel and UserModel, whereas the other two classes, if in separate files, would need to import GenericModel.

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

EntityModel --- has to import --> GenericModel

UserModel --- has to import --> GenericModel

I am curious if there is a way to refactor the code to achieve the same functionality while having the classes in separate .ts files.

Thank you!

Answer №1

To resolve the issue of circular dependencies, one effective approach is to separate them into their own module. All fromJson methods can be extracted into a new module, which can be converted into a class named ModelFactory, resembling a factory pattern. The revised structure would resemble the following:

export class ModelFactory {
  // Consider adding a constructor for configuration data

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

It is observed that the json object is typed as any, which may be too general considering that some properties of the type are known. It is recommended to define an interface for the json object, as demonstrated below:

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

type UUID = number;

Utilizing interfaces in this manner aligns more closely with typical TypeScript practices.

Similar questions

If you have not found the answer to your question or you are interested in this topic, then look at other similar questions below or use the search

How to leverage tsconfig paths in Angular libraries?

While developing an Angular library, I made configurations in the tsconfig.lib.json file by adding the following setup for paths: "compilerOptions": { "outDir": "../../out-tsc/lib", "target": "es2015", "declaration": true, "inlineSources ...

How come ts-jest in jest is affecting my JavaScript files?

As a newcomer to using jest with ts-jest, I am facing an error that I can't quite comprehend: $ jest FAIL src/lib/Core/Storage/JsonStorage.test.ts ● Test suite failed to run Jest encountered an unexpected token Jest failed to parse a f ...

Establishing a default value as undefined for a numeric data type in Typescript

I have a question regarding setting initial values and resetting number types in TypeScript. Initially, I had the following code snippet: interface FormPattern { id: string; name: string; email: string; age: number; } const AddUser = () => { ...

Issue: Interface not properly implemented by the service

I have created an interface for my angular service implementation. One of the methods in the service returns an observable array, and I am trying to define that signature in the interface. Here's what I have so far: import {Observable} from 'rxj ...

Creating a TypeScript interface where the type of one property is based on the type of another property

One of my Interfaces has the following structure: export enum SortValueType { String = 'string', Number = 'number', Date = 'date', } export interface SortConfig { key: string; direction: SortDirection; type: Sort ...

Manipulating Typescript JSON: Appending child attributes and showcasing them alongside the parent item attributes

Here is a JSON data that needs to be processed: var oldArr = [{ "careerLevel": "Associate", "careerLevels": [ { "201609": 21, "201610": 22, "careerID": "10000120" }, { "201609": 31, "201610": 32, ...

What is the best way to ensure smooth collaboration between OnChange and OnKeyDown?

I'm currently working on a weather app that integrates with the OpenWeather API and am facing a challenge with the search input functionality. My goal is to capture user input after they press the enter key. To achieve this, I'm using the OnChang ...

Guide on retrieving an ArrayList() from intricate function in Angular

Simplicity is the key to my question. Let's take a look at this Angular method: getAllOrdersHeaders(){ this.getAllOrdersIds().subscribe(idList=>{ idList.forEach(id=>{ this.ordersCollection.doc(id).collection('metadata&apo ...

Tips for narrowing down the type of SVG elements in a union

In my React project, I am facing an issue where I need to set a reference to an svg element that could be either a <rect>, <polygon>, or <ellipse>. Currently, I have defined the reference like this: const shapeRef = useRef<SVGPolygon ...

The error message indicates that the property 'current' is not found in the type '[boolean, Dispatch<SetStateAction<boolean>>]'

During my React/Typescript project, I encountered an issue involving cursor animations. While researching the topic, I stumbled upon a CodePen (Animated Cursor React Component) that functioned perfectly. However, when attempting to convert it into a Types ...

Angular2 Eclipse: Eclipse Oxygen's HTML editor detects TypeScript errors in real-time

After installing the Eclipse Oxygen plugin for Angular2, I created a project using the Angular CLI and opened it in Eclipse. However, when trying to convert the project to an Angular project, I couldn't find the option under configuration. Instead, th ...

The RxJS race function comes to a standstill if neither stream completes

Consider the code snippet below: import { interval, race, Subject } from 'rxjs'; import { mapTo } from 'rxjs/operators'; const a$ = new Subject<number>(); const b$ = interval(1000).pipe(mapTo(1)); race([a$, b$]).subscribe(consol ...

Looking for guidance on where to find a functional code sample or comprehensive tutorial on working with ViewMetadata in Angular2

I am currently trying to understand the relationship between viewmetadata and the fundamental use of encapsulation: ViewEncapsulation, including ViewEncapsulation.Emulated and ViewEncapsulation.None. Here is a link for further information: https://angula ...

While running tslint in an angular unit test, an error was encountered stating 'unused expression, expected an assignment or function call'

Is there a method to resolve this issue without needing to insert an ignore directive in the file? Error encountered during command execution: ./node_modules/tslint/bin/tslint -p src/tsconfig.json --type-check src/app/app.component.spec.ts [21, 5]: unuse ...

Set the default requests header in Ionic 3 using data stored in Ionic Storage

This particular issue is related to retrieving a value from local storage. I am trying to set the default header (Authorization token) for all requests, but I can't seem to find a solution that works efficiently. Most of the resources available only e ...

Tips for concealing material input

In my usual practice, when I need a form field to be part of the submission but not visible, I typically use <input type="hidden" /> However, when working with matInput, the option for a type of hidden is not available. While I could apply display: ...

Generate detailed documentation for the functional tests conducted by Intern 4 with automated tools

I need to automatically generate documentation for my Intern 4 functional tests. I attempted using typedoc, which worked well when parsing my object page functions. However, it failed when working with functional test suites like the one below: /** * Thi ...

Tips for verifying the variable type in a TypeScript ESLint custom rule

Trying my hand at creating a custom TypeScript-eslint rule that requires the callee's object of a node to be of a specific type, which I'll refer to as MyType. Utilizing the CallExpression from typescript-eslint in the code snippet below: Source ...

Guide on incorporating a bootstrap button into a React component using typescript

I am looking to create a custom React component using Typescript that wraps around a Bootstrap button. This component should allow me to pass parameters such as width, height, color, etc. These parameters would then be used to modify the appearance of the ...

Tips on integrating TypeScript into JavaScript

Currently, I am working with a node.js server.js file. var http = require('http'); var port = process.env.port || 1337; http.createServer(function (req, res) { res.writeHead(200, { 'Content-Type': 'text/plain' }); res ...