-
Notifications
You must be signed in to change notification settings - Fork 640
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
QueryBuilder.relate() and .patch() yield the wrong type #717
Comments
What leads you to that assertion? According to the docs, it should return a Promise to the number of related rows, not IDs. @koskimas should |
Ah, okay that may be correct about the number of related rows -- I was trying to infer that. The {
movieId: silverLinings.id,
personId: bradley.id
} UPDATE: That's what my koa-ts spinoff is doing. Let me confirm |
Yeah,
|
Is the output of |
QueryBuilder (Oddly, VSCode indicates that NOTE: I do need the patch count to know whether the target existed and the patch succeeded. When it doesn't exist, the query succeeds and return a count of zero. |
@jtlapp const rowsPatched: Promise<number> = qb.patch({}); |
|
It seems that const numUpdated: number = await Person.query().where('id', '<', 100).patch(obj); but this doesn't const numUpdated: number = await Person.query().patch(obj).where('id', '<', 100); Where methods return |
I think the whole Actually we would probably need three arguments:
maybe that would be too big of a mess... |
That way we could maybe get rid of the million query builder types. Also why is there a |
Actually it would be even nastier, but this works in almost all cases: interface SingleOrNot<M> {
single: M;
array: M[]
}
interface QueryBuilder<M, R, S extends keyof SingleOrNot<R>> extends Promise<SingleOrNot<R>[S]> {
where(): QueryBuilder<M, R, S>;
first(): QueryBuilder<M, R, 'single'>;
returning(): QueryBuilder<M, M, 'array'>
patch(): QueryBuilder<M, number, 'single'>
}
class Model {
}
let qb: QueryBuilder<Model, Model, 'array'>;
const x1: Promise<Model[]> = qb.where();
const x2: Promise<Model> = qb.where().first();
const x3: Promise<Model> = qb.first().where();
const x4: Promise<number> = qb.patch().where();
const x5: Promise<number> = qb.where().patch();
const x6: Promise<Model[]> = qb.where().patch().returning();
const x7: Promise<Model[]> = qb.patch().where().returning();
const x8: Promise<Model> = qb.patch().where().returning().first();
const x9: Promise<Model> = qb.patch().returning().first().where(); @mceachen What do you think? This would need to be refined some more, but maybe something to consider? |
No actually this would probably be enough. I don't remember why I thought the interface QueryBuilder<M, SR, R> extends Promise<R> {
where(): QueryBuilder<M, SR, R>;
first(): QueryBuilder<M, SR, SR>;
returning(): QueryBuilder<M, M, M[]>
patch(): QueryBuilder<M, number, number>
}
class Model {
}
let qb: QueryBuilder<Model, Model, Model[]>;
const x1: Promise<Model[]> = qb.where();
const x2: Promise<Model> = qb.where().first();
const x3: Promise<Model> = qb.first().where();
const x4: Promise<number> = qb.patch().where();
const x5: Promise<number> = qb.where().patch();
const x6: Promise<Model[]> = qb.where().patch().returning();
const x7: Promise<Model[]> = qb.patch().where().returning();
const x8: Promise<Model> = qb.patch().where().returning().first();
const x9: Promise<Model> = qb.patch().returning().first().where(); |
Yes, I'm sorry. Here's my problem code: return this.Model.query().patch(mods).where('id', personID).then((patchCount: any) => {
return (patchCount === 1); // TBD: remove 'any' type when patch type gets fixed
}); |
Despite QueryBuilder taking more parameters in the above, the typings may be easier to understand! I typically look to typedefs to help me understand what something is or does, in first pass, but I don't find these typedefs very helpful in that regard. Anything for more clarity! |
For now you can work around this by adding the |
The following tests all fail under the current typings. I have revised typings that makes them all pass, using a variation of @koskimas' above proposal. I'm just working on two more problems: query builder callback types and custom query builder types. const findByIdSelect: Promise<Person> = Person.query().findById(32).select('firstName').throwIfNotFound();
const findByIdJoin: Promise<Person> = Person.query().findById(32).join('tablename', 'column1', '=', 'column2').throwIfNotFound();
const findByIdJoinRaw: Promise<Person> = Person.query().findById(32).joinRaw('raw sql').throwIfNotFound();
const findOneWhere: Promise<Person> = Person.query().findOne('raw sql').where('more raw sql').throwIfNotFound(); // JTL
const findOneSelect: Promise<Person> = Person.query().findOne('raw sql').select('firstName').throwIfNotFound(); // JTL
const findOneWhereIn: Promise<Person> = Person.query().findOne('raw sql').whereIn('status', ['active', 'pending']).throwIfNotFound(); // JTL
const findOneWhereJson: Promise<Person> = Person.query().findOne('raw sql').whereJsonSupersetOf('x:y', 'abc').throwIfNotFound(); // JTL
const findOneWhereJsonIsArray: Promise<Person> = Person.query().findOne('raw sql').whereJsonIsArray('x:y').throwIfNotFound(); // JTL
const patchWhere: Promise<number> = Person.query().patch({firstName: 'Mo'}).where('id', 32); // JTL
const patchWhereIn: Promise<number> = Person.query().patch({firstName: 'Mo'}).whereIn('id', [1, 2, 3]); // JTL
const patchWhereJson: Promise<number> = Person.query().patch({firstName: 'Mo'}).whereJsonSupersetOf('x:y', 'abc'); // JTL
const patchWhereJsonIsArray: Promise<number> = Person.query().patch({firstName: 'Mo'}).whereJsonIsArray('x:y'); // JTL
const updateWhere: Promise<number> = Person.query().update({firstName: 'Mo'}).where('id', 32); // JTL;
const updateWhereIn: Promise<number> = Person.query().update({firstName: 'Mo'}).whereIn('id', [1, 2, 3]); // JTL;
const updateWhereJson: Promise<number> = Person.query().update({firstName: 'Mo'}).whereJsonSupersetOf('x:y', 'abc'); // JTL;
const updateWhereJsonIsArray: Promise<number> = Person.query().update({firstName: 'Mo'}).whereJsonIsArray('x:y'); // JTL;
const deleteWhere: Promise<number> = Person.query().delete().where('raw sql');
const deleteWhereIn: Promise<number> = Person.query().delete().whereIn('id', [1, 2, 3]); |
Query callbacks that don't need to return anything:
Actually all subquery or where grouping callbacks don't need to return anything and the return value and query builder return type are irrelevant. If there are other callbacks, ask. |
By the way, have you talked this refactoring through with @mceachen? He's the supreme overlord of typing related issues. I don't feel I can merge any large pull request regarding typings since mceachen has written 99% of them. |
Maybe a separate issue should be opened for this |
I hear from him once every day or two, which isn't enough interaction for me to get this done. So I'm just doing it and he can look it over. |
Yeah, turns out I'm doing this on my own time (like @koskimas). I pinged you on gitter, but send me an email and we can schedule a time together to share screen and bang out your issue list. |
Given the wide variety of return types available to For example, the typings might have it returning const numberOut: Promise<number> = <Promise<number>><Promise<any>>qb.relate(); |
The typings have the
relate()
method of QueryBuilder returning typethis
and thus yielding a Model. However,relate()
actually yields either a numberor an object pairing IDs to their corresponding reference fields. I don't know what the result is when providing multiple relations.UPDATE: QueryBuilder
patch()
has the same problem -- should yield a number.cc @mceachen
The text was updated successfully, but these errors were encountered: