olavim / objection-cursor

Cursor based pagination plugin for Objection.js
MIT License
30 stars 8 forks source link

Extending the query builder in TypeScript #42

Closed tony-oldport closed 1 year ago

tony-oldport commented 1 year ago

When I follow the objection.js recipe for extending the query builder in TypeScript, I get the following compilation error:

TypeError: Cannot set property QueryBuilder of class extends Base {
                         static get QueryBuilder() {
                                 return CursorQueryBuilder;
                         }
                 } which has only a getter

I've tried looking through this issue and the ts rewrite branch, and it feels like there is a solution in there somewhere. But, I'm just having trouble putting all the pieces together.

Here are the relevant lines in my base Model class:

class BaseQueryBuilder<M extends Model, R = M[]> extends QueryBuilder<M, R> {
  ArrayQueryBuilderType!: BaseQueryBuilder<M, M[]>
  SingleQueryBuilderType!: BaseQueryBuilder<M, M>
  MaybeSingleQueryBuilderType!: BaseQueryBuilder<M, M | undefined>
  NumberQueryBuilderType!: BaseQueryBuilder<M, number>
  PageQueryBuilderType!: BaseQueryBuilder<M, Page<M>>

  setRequestorOrgId (requestorOrgId: string | number): this {
    this.context({ requestorOrgId }).catch(() => {})
    return this
  }
}

const cursor = cursorMixin(CURSOR_PAGINATION_CONFIG)
type CursorModelConstructor<T> = new (...args: any[]) => T
function CursorModel<T extends CursorModelConstructor<Model>> (Base: T): T {
  return cursor(Base)
}

export class BaseModel extends CursorModel(Model) {
  QueryBuilderType!: BaseQueryBuilder<this>
  static QueryBuilder = BaseQueryBuilder

...
tony-oldport commented 1 year ago

OK, I may have actually pieced it together. As far as I can tell, this is working well, but still need to do a lot of testing. I do have to suppress compiler errors in a couple spots and haven't found a better way to handle those.

Thanks to everyone who provided a roadmap in the links above.

const CursorModel = compose([cursorMixin(CURSOR_PAGINATION_CONFIG)])(Model)
// @ts-expect-error
class BaseQueryBuilder<M extends Model, R = M[]> extends CursorModel.QueryBuilder<M, R> {
  ArrayQueryBuilderType!: BaseQueryBuilder<M, M[]>
  SingleQueryBuilderType!: BaseQueryBuilder<M, M>
  MaybeSingleQueryBuilderType!: BaseQueryBuilder<M, M | undefined>
  NumberQueryBuilderType!: BaseQueryBuilder<M, number>
  PageQueryBuilderType!: BaseQueryBuilder<M, Page<M>>

  cursorPage (cursor?: string | null, before = false): any {
    // @ts-expect-error
    return super.cursorPage(cursor, before)
  }

  setRequestorOrgId (requestorOrgId: string | number): this {
    this.context({ requestorOrgId }).catch(() => {})
    return this
  }
}

export class BaseModel extends CursorModel {
  QueryBuilderType!: BaseQueryBuilder<this>
  static get QueryBuilder (): any {
    return BaseQueryBuilder
  }

...
}