When generating an Entity (Configuration) containing an enumeration (Status), the Angular model looks like this :
const enum Status {
'DRAFT',
'DONE',
'ARCHIVED'
}
export class Configuration implements BaseEntity {
constructor(
public id?: number,
public json?: string,
public status?: EventConfigurationStatus,
) {
}
}
The problem is that the enum is not exported. Because I want to use the enum on its own, I add the export keyword. This is hard-coded in _entity.model.ts at line 86
Won't you think that having export by default would be better ?
Yes it makes sense.
Beside you use case, it would be also required for JHipster entity generator in order to generate compiling code if you had a Customer entity with a relationship with Configuration and using "otherEntityField": "status".
Related
how can I use a constructor on an Entity that has properties with #ManyToOne decorator and their types are from another Entity but only with the primary key.
For example:
#Entity()
class User {
constructor(
idUser: number,
idExtraData: number
){
this.id = idUser;
// this is going to give an error because it requires an instance of ExtraData.
// but I would like to pass only the id
// and have an instance similar when I use findOne without populating anything
this.extraData = idExtraData;
}
#PrimaryKey()
id: number;
#ManyToOne()
extraData: ExtraData;
}
The instance of the entity I want should be similar to the instance returned by findOne without populating anything.
You can use Reference.createNakedFromPK()
#Entity()
export class Book {
#ManyToOne(() => Author)
author: Author;
constructor(authorId: number) {
this.author = Reference.createNakedFromPK(Author, authorId);
}
}
This instance will be either replaced with the managed entity during flush if there is one, or it will be merged to the EM otherwise.
(the docs mention how to do this with reference wrapper only, but its the same approach)
https://mikro-orm.io/docs/entity-references#assigning-to-reference-properties
I am building a backend using Parse Server written in Typescript. Parse uses "magic strings" in many places and I am trying to avoid them.
For example, if I have a Parse Class named Item and it has an attribute color, I would get the property the following way: myItem.get('color').
I am hoping to make this type safe and use an enum for color instead of the string itself. I already have an interface defined for all attributes of Item like so:
import { Attributes, Object } from 'parse'
export interface ItemAttributes extends Attributes {
color: string
size: number
enabled: boolean
}
export class Item extends Object<ItemAttributes> {
constructor (data?: Partial<ItemAttributes>) {
super('Item', data as ItemAttributes)
}
}
Is there a way to generate an enum from the ItemAttributes interface in Typescript without having to create a new enum for every Parse Class like so?
export enum ItemKeys {
color = 'color'
size = 'size'
enabled = 'enabled'
}
As a result I am hoping to be able to use my new enum in the Parse getter like this:
myItem.get(ItemKeys.color)
I am working with Nest.js + TypeORM and hit a snag when trying to add inheritance to service classes.
I want to have a User service class that extends off of a Base service class, inheriting all the methods the it has.
This is what I've done:
export class BaseService<T> {
private repo;
constructor(repo: Repository<T>){
this.repo = repo;
}
async findAll(opts?): Promise<T[]> {
return this.repo.find(opts);
}
......
}
Then on my User service:
export class UserService extends BaseService<User> {
constructor(
#InjectRepository(User)
private userRepository: Repository<User>,
private readonly mailerService: MailerService,
) {
super(userRepository);
}
}
This works fine where I just need a single repository in the Service class but once I need more such as productRepository, as you can see it would fail due to constructor being hardcoded to accept a single repository.
I can't seem to figure out what would be the most elegant way of achieving something like this.
Does anyone know?
It is an old thread, anyways I meat this same problem and this answer may help someone else...
You can have an abstract class with common functions and boilerplate that you wich to be available in all your repositories to maintain patterns and so on...
export abstract class BaseService<T extends ObjectLiteral> {
protected repo: Repository<T>;
async findAll(opts?): Promise<T[]> {
return this.repo.find(opts);
}
......
}
Observe that constructor is omitted here and as an abstract class you should never have an instance of it, it is meant to be just an extension with common boilerplates and features that you don't want to write every single time you need a new service with the same features like a CRUD service or so...
Then you will use it like this:
export class UserService extends BaseService<User> {
constructor(
#InjectRepository(User) protected readonly repo: Repository<User>,
private readonly mailerService: MailerService,
) {
super();
}
...... extended stuff
}
I'm not sure if you need to inject repo with the same name and visibility but as it worked for me I didn't dive any further to get to know...
The bad side of this approach to me is that even if you don't have anything else to inject via constructor in your service you need to declare constructor and inject the base repository... it is a boilerplate that could be avoided too but I couldn't achieve this right now...
I am trying to build an "Entity Framework"-like ORM library for node.js and MongoDB with TypeScript.
With this library, the consumer will be able to define a Model class (ex. Person) that will extend a Base class, and the class decorator will add additional data to the Person class (for example, instances array, that will contain all the Model's instances, and collection_name that will be the MongoDB collection name for the model, etc.).
Code in TypeScript playground.
So my first step was creating a Base class:
class Base<T>
{
static collection_name: string
static instances: Base<any>[]
_id: string
}
So the user will be able to define his model like so:
#decorator<Person>({collection_name: 'people'})
class Person extends Base<Person>
{
#field name
#field address
...
}
then I created a decorator class to set the collection_name and instances properties on the Person class:
function decorator<T>(config: { collection_name: string }) {
return function <T extends Base<T>>(Class: IClass<T>) {
Class.collection_name = config.collection_name;
Class.instances = [];
return Class
}
}
the decorator function receives the user-generated Class, and I am trying to create an interface that will describe the type of such class. I called it IClass:
interface IClass<T>
{
new(): Base<T>
instances: Base<T>[];
collection_name: string
}
new is the constructor (that returns a Base instance)
instances and collection_name are static properties of Base<T> and are non-static here (I'm not sure about this, is this right?)
However, when trying to define the user Model I get the following error:
#decorator<Person>({collection_name: 'people'}) // <==== ERROR HERE ===
class Person extends Base<Person>
{
}
Error:(23, 2) TS2345: Argument of type 'typeof Person' is not
assignable to parameter of type 'IClass>'.
Property 'instances' is missing in type 'typeof Person' but
Type 'typeof Person' is missing the following properties from type
required in type 'IClass>'.
It seems like the typescript compiler is ignoring the static members inherited from Base when checking the type of typeof Person.
How can I define the type of the Class property of the decorator function ?
The problem as jcalz points out is that your decorator is accepting a Class of a type that already has the static properties instances and collection_name. You need to use two different interfaces, one which is a type that simply constructs instances of T with the new(): T signature, and another that extends this interface to include the static properties your decorator will add.
class Base<T> {
static _id = 0;
private _id: number;
constructor () {
this._id = Base._id++;
}
}
interface BaseConstructor<T extends Base<T>> {
_id: number;
new(): T;
}
interface DecoratedBaseConstructor<T extends Base<T>> extends BaseConstructor<T> {
instances: T[];
collection_name: string;
}
function decorator<T extends Base<T>>(config: { collection_name: string }) {
return (baseConstructor: BaseConstructor<T>): DecoratedBaseConstructor<T> => {
const decoratedBaseConstructor = baseConstructor as Partial<DecoratedBaseConstructor<T>>;
decoratedBaseConstructor.collection_name = config.collection_name;
decoratedBaseConstructor.instances = [];
return decoratedBaseConstructor as DecoratedBaseConstructor<T>;
};
}
#decorator<Person>({collection_name: 'people'})
class Person extends Base<Person> {
name: string;
constructor () {
super();
this.name = 'foo';
}
}
With this approach, all of Base's static members must be public. Any static members of Base initialized in the decorator should go in the DecoratedBaseConstructor, and any remaining static members not initialized in the decorator should go in the BaseConstructor instead.
I assume that you use the generic type T in the Base class somehow in your actual code, but if you don't, you should remove the generic type from the Base class and everything else will still work the same.
Check out the above snippet in this playground.
When we have several entities with different relationships between them (eg. Event --1-> Venue --*-> Rooms) JHipster generates the following for the Java back-end, which is fine :
#Entity
public class Event implements Serializable {
#ManyToOne
private Venue venue;
}
#Entity
public class Venue implements Serializable {
#OneToMany(mappedBy = "venue")
private Set<Room> rooms = new HashSet<>();
}
#Entity
public class Room implements Serializable {
}
The equivalent model in Angular is not as strongly typed. Instead, the model uses the BaseEntity when there is a relation :
export class Event implements BaseEntity {
constructor(public venue?: BaseEntity) {}
}
export class Venue implements BaseEntity {
constructor(public rooms?: BaseEntity[]) {}
}
export class Room implements BaseEntity {
constructor( ) {}
}
With TypeScript we would highly benefit to type this code so we could navigate between objects, such as :
this.event.venue.rooms;
this.event.venue.rooms[0].name;
It would be a matter of generating the model classes without BaseEntity but the classes themselves :
export class Event implements BaseEntity {
constructor(public venue?: Venue) {}
}
export class Venue implements BaseEntity {
constructor(public rooms?: Room[]) {}
}
export class Room implements BaseEntity {
constructor( ) {}
}
WDYT ? Is there a reason why the Angular model is not as typed as the Java one ?
The original reason for introducing the BaseEntity(It was by me) was to avoid circular reference in Typescript.
The build was taking unusually long and sometimes was crashing when two entities had relationships with each other resulting in the Entity model being imported in each other for the relation. This was the quick solution I could find at that time to avoid the circular reference.
Note: This was done some time back and maybe the problem doesn't exist anymore with the latest Typescript and Webpack so probably this can be revisited.