Reputation: 20967
I'm trying to write a generic repository in TypeScript that serializes/deserializes using localStorage.
I've read many related questions regarding new()
in ts, but they are full of foos, bars and bazes, and I can't find a real example. Most importantly, I can't find an example that can create a new instance inside a generic class (all examples I found assume the type is known, whereas as you'll see below the type is unknown in the repository class).
A "Dog" entity:
interface IEntity { }
class Dog implements IEntity {
constructor(json: string); // called when deserializing
constructor(name: string, age: number);
constructor(jsonOrName: string, age?: number) { /* implementation... */ }
name: string;
age: number;
toJSON() { // called when serializing (via JSON.stringify)
//...
}
}
And a repository to serialize/deserialize to/from localStorage.
class Repository<T extends IEntity> {
constructor(private key: string) { }
read(): T | null {
const s = localStorage.getItem(this.key);
if (!s) return null;
const value = JSON.parse(s);
return new T(value); // <----------- how do I do this?
}
write(value: T): void {
localStorage.setItem(this.key, JSON.stringify(value));
}
}
The intended use is:
const dogRepository = new Repository<Dog>("dog");
const dog = dogRepository.read();
if (dog) console.log(dog.name);
Upvotes: 0
Views: 114
Reputation: 329598
The type system is completely erased at runtime, so the type named T
will not exist as anything you can construct via the new
operator. Instead you need your Respository<T>
instances to hold an actual runtime constructor for your T
. For example:
class Repository<T extends IEntity> {
// take a key *and* a constructor which 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 ctor on the JSON (don't parse, right?)
}
write(value: T): void {
localStorage.setItem(this.key, JSON.stringify(value));
}
}
And then you'd have to change this as well:
const dogRepository = new Repository("dog", Dog); // pass ctor here, T is inferred
const dog = dogRepository.read();
if (dog) console.log(dog.name);
Does that make sense? Hope that helps. Good luck!
Upvotes: 2