Search code examples
typescriptdatabase-designnestjstypeorm

Create mapping table in TypeORM where 1 column can reference multiple tables


I want to create this design in TypeORM entities:

database design

The idea is that a list can contain any number of items that can be of any type. There are two types of items right now: todos and hires. There may be more in the future.

These are the list, todo and hire entities:

@Entity()
export class List {
  @PrimaryGeneratedColumn()
  id: number

  @OneToMany(() => Item, (item) => item.list)
  items: Item[]

  @Column({
    length: 60,
  })
  title: string
}

@Entity()
export class Todo {
  @PrimaryGeneratedColumn()
  id: number

  @OneToOne(() => Item)
  item: Item

  @Column({
    length: 60,
  })
  title: string

  @Column('text')
  description: string

  @Column({ default: false })
  done: boolean
}

@Entity()
export class Hire {
  @PrimaryGeneratedColumn()
  id: number

  @OneToOne(() => Item)
  item: Item

  @Column({
    length: 60,
  })
  title: string

  @Column('datetime')
  startDate: string

  @Column('datetime')
  endDate: string
}

In the mapping table, how do I let TypeORM know what type the entityId can be?

@Entity()
export class Item {
  @PrimaryGeneratedColumn()
  id: number

  @ManyToOne(() => List, (list) => list.items)
  @JoinColumn()
  list: List

  @Column()
  entityType: string

  @OneToOne(() => Todo /* Todo OR Hire */, (itemType) => itemType.item)
  entity: Todo // Todo OR Hire
}

Can this whole problem can be circumvented by doing something different?


Solution

  • I may be missing details, but I wonder if it wouldn't work with inheritance, based on the way you described the problem:

    List

    @Entity()
    export class List {
      @PrimaryGeneratedColumn()
      id: number
    
      @OneToMany(() => Item, (item) => item.list)
      items: Item[]
    
      @Column({
        length: 60,
      })
      title: string
    }
    

    Item

    @Entity()
    export abstract class Item {
      @PrimaryGeneratedColumn()
      id: number
    
      @ManyToOne(() => List, (list) => list.items)
      @JoinColumn()
      list: List
    
      @Column()
      entityType: string
    }
    

    Todo

    @Entity()
    export class Todo extends Item {
      @PrimaryGeneratedColumn()
      id: number
    
      @Column({
        length: 60,
      })
      title: string
    
      @Column('text')
      description: string
    
      @Column({ default: false })
      done: boolean
    }
    

    Hire

    @Entity()
    export class Hire extends Item {
      @PrimaryGeneratedColumn()
      id: number
    
      @Column({
        length: 60,
      })
      title: string
    
      @Column('datetime')
      startDate: string
    
      @Column('datetime')
      endDate: string
    }