A versatile repository that generates objects of a parametric type

I'm currently working on developing a generic repository in TypeScript that involves serialization and deserialization using localStorage.

While researching, I have come across several relevant discussions regarding the use of new() in ts. However, most examples are filled with placeholders like foos, bars, and bazes, making it challenging to find a concrete demonstration of this concept. In particular, I am struggling to find an example demonstrating how to create a new instance within a generic class (as opposed to assuming that the type is already known).

Let's start with a simple "Dog" entity:

interface IEntity { }

class Dog implements IEntity {

  constructor(json: string);     // invoked during deserialization
  constructor(name: string, age: number);
  constructor(jsonOrName: string, age?: number) { /* implementation... */ }

  name: string;
  age: number;

  toJSON() {    // utilized for serialization (via JSON.stringify)
    //...
  }
}

We also have a repository responsible for handling serialization and deserialization to/from localStorage.

class Repository<T extends IEntity> {

  constructor(private key: string) { }

  read(): T | null {
    const storedData = localStorage.getItem(this.key);
    if (!storedData) return null;
    const value = JSON.parse(storedData);
    return new T(value);            // <----------- in need of guidance here
  }

  write(value: T): void {
    localStorage.setItem(this.key, JSON.stringify(value));
  }

}

The intended usage scenario is as follows:

const dogRepository = new Repository<Dog>("dog");
const retrievedDog = dogRepository.read();
if (retrievedDog) console.log(retrievedDog.name);

Answer №1

At the runtime, the type system is entirely erased. This means that the type labeled T does not exist in a form you can create using the new operator. Instead, your instances of Repository<T> need to contain an actual constructor for your T. For instance:

class Repository<T extends IEntity> {

  // accepts a key and a constructor that operates on a JSON string
  constructor(private key: string, private ctor: new (json: string) => T) {}

  read(): T | null {
    const s = localStorage.getItem(this.key);
    if (!s) return null;
    return new this.ctor(s); // use the constructor on the JSON directly (no parsing)
  }

  write(value: T): void {
    localStorage.setItem(this.key, JSON.stringify(value));
  }
}

Furthermore, you would need to adjust this as well:

const dogRepository = new Repository("dog", Dog); // provide the constructor here, T is automatically determined
const dog = dogRepository.read();
if (dog) console.log(dog.name);

Does this explanation make sense? I hope it clarifies things for you. Best of luck!

Link to code

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

Tips for accessing files following the transmission of a post request within the req.body

Encountering a problem where image uploads to an s3 bucket are not successful. The error message received is: API resolved without sending a response for /api/upload/uploadPhoto, this may result in stalled requests. The front end includes an input that ca ...

Error encountered while installing node modules within an angular workspace

Currently, I am facing an issue with my workspace where the command npm install is giving me a series of errors that I cannot seem to resolve. I have tried running it as an admin, manually deleting the node_modules folder, asking for help from a senior col ...

Is it possible for pdfjs-dist to be used with Typescript?

Is there a way to preview a PDF as a canvas without importing pdfjs-dist into my project? I have already used the command $yarn add pdfjs-dist to install pdfjs-dist. Do I need to include any additional imports? import pdfjsLib from "pdfjs-dist/build ...

Sending template reference from one Angular component to another

I have a main grid component that includes smaller grid-item components. The majority of these grid items navigate to a specific route when clicked. However, there is one particular item that should open a modal window instead of navigating. Is there a wa ...

Encountering TypeScript errors when trying to reference Angular2 within a Gulp setup

The issue at hand is: [11:16:06] TypeScript: 103 semantic errors [11:16:06] TypeScript: emit succeeded (with errors) I am currently using node v5.7.0 and npm 3.6.0 gulp -v: [11:26:58] Requiring external module babel-register [11:26:58] CLI version 3.9 ...

Creating number inputs in Ionic 2/3 using alerts

I am currently working with Ionic 3.x on my macOS system. The issue I am facing is as follows: I have an array that contains a number and another array consisting of names. table: { number: number, names: string[] } = { number: 0, names: ['& ...

Effortlessly collapsing cards using Angular 2 and Bootstrap

Recently delving into Angular 2 and Bootstrap 4, I set up an about page using the card class from Bootstrap. Clicking on a card causes it to expand, and clicking again collapses it. Now, I want to enhance this by ensuring that only one card is open at a ti ...

The code coverage report for Istanbul indicates that there is an uncovered branch within the constructor function

Currently, I am conducting a test on my node.js written TypeScript code using mocha and chai. To measure the code coverage, I have incorporated nyc. Moreover, in this project, I am utilizing Typeorm as an ORM tool and implementing inversify as an IoC cont ...

The instance is referencing property or method "foo" during render, but it has not been defined. To ensure reactivity, please make sure that this property is defined

Just starting out with the Vue framework and experimenting with component composition. However, I'm encountering an issue when trying to run the code: "Property or method "icons" is not defined on the instance but referenced during render. Make sure ...

Utilizing a TypeScript Variable as a Tagname in an HTML File within Angular

This specific problem is originally documented in this post. Despite being flagged as a duplicate, my scenario differs because the HTML content at hand is too extensive for utilizing innerHTML. The structure of my component's HTML file is as follows: ...

Comparing ESLint and TSLint: Which One Is Right for You

After looking through numerous sources, I came up empty-handed regarding this particular question. Could anyone provide insight on the drawbacks of using Eslint compared to TsLint? What are the reasons for transitioning to ESLint? ...

What is the best way to track events in angular-meteor when a user logs in, logs out, or when there is a change in the user

I am working on meteor-angular and trying to track new user login and logout changes within a single component. I have attempted to subscribe to userData in the component's initialization, but it does not seem to detect when the user logs in or out. I ...

ngOnChanges will not be triggered if a property is set directly

I utilized the modal feature from ng-bootstrap library Within my parent component, I utilized modalService to trigger the modal, and data was passed to the modal using componentInstance. In the modal component, I attempted to retrieve the sent data using ...

Integrating Typescript into function parameters

I am attempting to make my function flexible by allowing it to accept either a string or a custom type onPress: (value: string | CustomType)=>void But when I try to assign a string or CustomType, the compiler gives an error saying is not assignable to ...

My goal is to create a carousel using Vue 3 with the Composition API and TypeScript

Creating a carousel with Vue 3 and TypeScript has been quite challenging for me. I heard about using "vue-awesome-swiper" to build a carousel, but I couldn't find a tutorial on how to use it. Does anyone know how to utilize this tool effectively? Alte ...

I'm experiencing an issue with redirect in Nextjs that's causing an error message to appear. The error reads: "SyntaxError: JSON.parse: unexpected character at line 1 column 1 of the JSON data

I'm currently diving into the world of NextJS and working on creating a simple recipe application. Utilizing the new App Router has been smooth sailing for the most part, except for one hiccup with the login function. After successfully logging in (us ...

Refreshing a page with a 404 error in Angular 2 while in production mode and without the useHash configuration

I've encountered an issue while using Angular 2 without the useHash feature. When trying to visit the URL directly in a browser, I'm getting a 404 not found error. I have searched extensively and attempted various solutions including: Adding L ...

How can I simulate or manipulate the element's scrollHeight and clientHeight in testing scenarios?

In my JavaScript code, I have a function that checks if an HTML paragraph element, 'el', is a certain size by comparing its scrollHeight and clientHeight properties: function isOverflow(element: string): boolean { const el = document.getEleme ...

Can you point me in the direction of the source code for dependencies stored in the node_modules

I'm new to TypeScript and NPM, so my question might seem basic. I've added some dependencies to my package.json file and used the npm install command to install them. After installation, I noticed that many dependencies were added to the node_mod ...

Why is the selected option not visible in the Angular 8 drop-down?

This is a commonly asked question, but my situation seems to be unique. None of the existing answers have provided a solution for my specific issue. Here is the code that I am working with: <form (ngSubmit)="getExceptions(f)" #f="ngForm"> ...