[SOLVED] How to take property from object or autogenerate using conditional types?

Issue

I look for creating an Entity type (possibly using conditional types) which generates a property if it does not exist:

type EntityIdType = number | string

type EntityWithId = {
  id: EntityIdType
}

type EntityWithoutId = {}

export type EntityMap<E> = {
  [id: EntityIdType]: E;
}

class EntityTable<E extends Entity> {
   byId: EntityMap<E> = {}

   insert = (entity: Entity): EntityIdType => {
      const id = ... // I only got it using entity.hasOwnProperty(), sure there is a better way
      this.byId[id] = entity
      return id
   }

   get = (id: EntityIdType): Entity => {
      this.byId[id] // I only got it comparing with undefined and using 'as'
   }
}

const tableWithId = new EntityTable<EntityWithId>()
tableWithId.insert({ id: 'myId', foo: 'bar' })
console.log(tableWithId.get('myId')) // { id: 'myId', foo: 'bar' }

const tableWithoutId = new EntityTable<EntityWithoutId>()
const id = tableWithoutId.insert({ foo: 'bar2' })
console.log(tableWithoutId.get(id)) // { id: 3244, foo: 'bar' }

My almost working (does not insert id in EntityWithoutId) but not quite good code for Entity and EntityTable:

type Entity<T extends EntityIdType | undefined> = T extends EntityIdType
  ? EntityWithId
  : EntityWithoutId

export class EntityTable<E extends Entity<EntityIdType | undefined>> {
  private static autoIncrementId = new AutoIncrementId()

  byId: EntityMap<E> = {}
  allIds: EntityIdType[] = []

  insert = (entity: E): EntityIdType => {
    const id = entity.hasOwnProperty('id') ? (entity as EntityWithId).id : EntityTable.autoIncrementId.new()
    this.byId[id] = entity
    this.allIds.push(id)

    return id
  }

  getByEntityId = (id: EntityIdType): Entity<EntityIdType> | undefined =>
    this.byId[id] === undefined ? undefined
       : this.byId[id] as Entity<EntityIdType>
}

Solution

Your main problem was that you were not adding the id to your entity after generating it. Here is a slightly less verbose version of your EntityTable.

class EntityTable<Entity extends { id?: EntityIdType }> {
  private static autoIncrementId = new AutoIncrementId()

  byId: EntityMap<Entity & { id: EntityIdType}> = {}
  allIds: EntityIdType[] = []

  insert = (entity: Entity): EntityIdType => {
    const id = entity.id || EntityTable.autoIncrementId.new()
    this.byId[id] = { ...entity, id }
    this.allIds.push(id)

    return id
  }

  get = (id: EntityIdType) => this.byId[id]
}

Answered By – ScarpMetal

Answer Checked By – Clifford M. (BugsFixing Volunteer)

Leave a Reply

Your email address will not be published. Required fields are marked *