-
Notifications
You must be signed in to change notification settings - Fork 639
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
[Objection2] Documentation request for plugin in TypeScript #1589
Comments
It's basically impossible to type plugins unfortunately. Especially if multiple plugins are used. There's no way to extend an existing dynamic type in typescript. A plugin is a function that takes a model class constructor and returns a new constructor inherited from the input class: function somePlugin<T extends Model>(modelClass: ModelClass<T>): ??? {
return class extends modelClass {
...
}
} The class Person extends somePlugin(Model) {
} and class Person extends somePlugin(someOtherPlugin(Model)) {
} If the plugin doesn't add any new methods to neither the function somePlugin<T extends typeof Model>(modelClass: T): T {
...
} |
That is, unfortunately, what I have come to realize after many hours of trying to type them. Even after lowering my expectations and peppering a few As an aside, one thing I was using with some level of success before was Is it no longer required to have that duplication? I believe it was there before to deal with |
The old |
That is what I thought. Thanks. I'd like to write some docs anyway with some examples along with the drawbacks. Would that be okay? |
hmm... I was able to create this.. Maybe it's possible nowadays after all import { Model, QueryBuilder, Page } from './typings/objection';
type Constructor<T extends Model> = new (...args: any[]) => T;
class CustomQueryBuilder<M extends Model, R = M[]> extends QueryBuilder<M, R> {
ArrayQueryBuilderType!: CustomQueryBuilder<M, M[]>;
SingleQueryBuilderType!: CustomQueryBuilder<M, M>;
NumberQueryBuilderType!: CustomQueryBuilder<M, number>;
PageQueryBuilderType!: CustomQueryBuilder<M, Page<M>>;
someCustomMethod(): this {
return this;
}
}
function mixin<T extends Constructor<Model>>(ModelClass: T) {
return class extends ModelClass {
static QueryBuilder = QueryBuilder;
QueryBuilderType: CustomQueryBuilder<this, this[]>;
mixinMethod() {}
};
}
class Person extends Model {}
const MixinPerson = mixin(Person);
async () => {
const z = await MixinPerson.query()
.whereIn('id', [1, 2])
.someCustomMethod()
.where('foo', 1)
.someCustomMethod();
z[0].mixinMethod();
}; |
This was with typescript 3.7.2 |
That is actually really close to what I have. I'll update my stuff to reflect your example and let you know if there are any issues. I am also on 3.7.2. |
So here is the example I am working with. I'll annotate with the errors I am seeing: import { Model } from 'objection';
export type ModelConstructor<T extends Model = Model> = new (
...args: any[]
) => T;
export function Mixin(options = {}) {
return function<T extends ModelConstructor>(Base: T) {
class CustomQueryBuilder<M extends Model, R = M[]> extends QueryBuilder<M, R> {
ArrayQueryBuilderType!: CustomQueryBuilder<M, M[]>;
SingleQueryBuilderType!: CustomQueryBuilder<M, M>;
NumberQueryBuilderType!: CustomQueryBuilder<M, number>;
PageQueryBuilderType!: CustomQueryBuilder<M, Page<M>>;
// Class 'QueryBuilder<M, R>' defines instance member property 'delete', but extended class 'CustomQueryBuilder<M, R>' defines it as instance member function.ts(2425)
delete() {
const value = new Date();
// Argument of type '{ [x: string]: unknown; }' is not assignable to parameter of type 'PartialModelObject<this["ModelType"]>'.ts(2345)
return this.patch({
"deleted_at": value
});
}
kept(): this {
// Property 'ref' does not exist on type 'T'.ts(2339)
return this.whereNull(Base.ref(column));
}
discarded(): this {
// Property 'ref' does not exist on type 'T'.ts(2339)
return this.whereNotNull(Base.ref(column));
}
}
return class extends Base {
static QueryBuilder = CustomQueryBuilder;
QueryBuilderType!: CustomQueryBuilder<this, this[]>;
static get modifiers(): Modifiers<CustomQueryBuilder<Model>> {
return {
// Property 'modifiers' does not exist on type 'T'.ts(2339)
...super.modifiers,
discarded(builder) {
builder.discarded();
},
kept(builder) {
builder.kept();
}
};
}
};
};
} Based on these errors, it feels like TypeScript just can't figure out the correct type for |
The export function Mixin(options = {}) {
return function<T extends typeof Model>(Base: T) { |
I was using // Class 'QueryBuilder<M, R>' defines instance member property 'delete', but extended class 'CustomQueryBuilder<M, R>' defines it as instance member function.ts(2425)
delete() {
const value = new Date();
// Argument of type '{ [x: string]: unknown; }' is not assignable to parameter of type 'PartialModelObject<this["ModelType"]>'.ts(2345)
return this.patch({
"deleted_at": value
});
} The second error |
The first error is now fixed in 2.0.3 |
Thank you! I ended up doing an assertion for the second error: this.patch({
"deleted_at": value
} as PartialModelObject<T>); Not the worst compromise to me... |
That error makes complete sense. There is no |
@koskimas Yes, you are right. I got mixed up since I simplified it for the example. In the code I am using, that column is dynamic based on options passed to the decorator. And I have yet to find a way to dynamically add properties to classes in that way via a decorator. |
Closes #1589 Signed-off-by: Will Soto <willsoto@users.noreply.github.com>
Fyi @koskimas, generating declaration files ( |
There is currently a "recipe" of how to write a plugin (essentially a mixin) using regular JavaScript but I think it would be nice to have a TypeScript example just like you do for the query builder.
The text was updated successfully, but these errors were encountered: