3

In my app, I want user to have notification items (news) of different type. User and NewsItem must have one-to-many relationship, but also, NewsItem is just a base class for the different types of actual news items.

Here's my base NewsItem class:

@Entity()
@TableInheritance({ column: { type: "varchar", name: "type"}})
export class NewsItem {

    @PrimaryGeneratedColumn()
    id: number;

    @ManyToOne(type => User, user => user.news)
    receiver: Promise<User>;

}

Here's the User it's attached to:

@Entity()
export class User {

    @PrimaryGeneratedColumn()
    id: number;

    // (a lot of irrelevant stuff removed)

    @OneToMany(type => NewsItem, newsItem => newsItem.receiver)
    news: Promise<NewsItem[]>;

}

And here's an example of a concrete news item:

@ChildEntity()
export class ConcreteNewsItem extends NewsItem {

    @Column()
    someData: number;
}

It all seems straightfoward enough from the documentation. Here's what I don't get, however: how do I go through a user's news, check what exact type each of them is, and get an object of a concrete news item type for each of them?

As I understand it, TypeORM will create a column called type in SQL definition of news_item table - but this column is not available on NewsItem class itself, and I can't understand how to build a query that would actually give me the ConcreteNewsItem objects!

Max Yankov
  • 12,551
  • 12
  • 67
  • 135
  • I'm trying to implement this - did you find a solution? – maxpaj May 15 '20 at 14:19
  • @maxpaj not an elegant one, no. For this particular case, I ended up manually building tables for all child types and then iterating over all child types when I need news of all types - which is pretty ugly, but works. For another case, I built a base class table with enum type field and then a table for each child class, with a foreign key of (id, type) from base class table, with constraint that guarantees that type will be this particular child type. However, TypeORM handles postgresql enums very badly, so it's a lot of pain to maintain. – Max Yankov May 15 '20 at 22:40

1 Answers1

5

Old question, but still unanswered. I found a somewhat elegant solution to this. TypeORM in fact queries the concrete types of the entities. You are able to know which concrete type the result entity of the query is by comparing constructor name to the class name. Just like this:

const allNews = await repository.find(); //Repository pattern for the query
for (const news in allNews) {
   if (news.constructor.name == ConcreteNewsItem.name){
       const concreteNews = news as ConcreteNewsItem; //This cast is safe
   }
   else if(...)
   ...
}

Hope this helps someone.

Edit:

Better yet, you can use instanceof and it works because TypeORM in fact uses the concrete class constructor.

Ignacio Ruiz
  • 611
  • 10
  • 21