K
Kysely•3mo ago
mike

Query fragmentation causes typing to break

Hi, can anybody help how to make fragmented joins working? https://kyse.link/SVDlH
let query_ok = db
.selectFrom("person")
.innerJoin("pet", "owner_id", "person.id");
query_ok = query_ok.innerJoin("toy", "toy.pet_id", "pet.id");
let query_ok = db
.selectFrom("person")
.innerJoin("pet", "owner_id", "person.id");
query_ok = query_ok.innerJoin("toy", "toy.pet_id", "pet.id");
let query_fails = db.selectFrom("person");
query_fails = query_fails.innerJoin("pet", "owner_id", "person.id");
query_fails = query_fails.innerJoin("toy", "toy.pet_id", "pet.id"); // Argument of type '"pet.id"' is not assignable to parameter of type 'AnyJoinColumn<Database, "person", "toy"> | AnyJoinColumnWithTable<Database, "person", "toy">'.(2345)
let query_fails = db.selectFrom("person");
query_fails = query_fails.innerJoin("pet", "owner_id", "person.id");
query_fails = query_fails.innerJoin("toy", "toy.pet_id", "pet.id"); // Argument of type '"pet.id"' is not assignable to parameter of type 'AnyJoinColumn<Database, "person", "toy"> | AnyJoinColumnWithTable<Database, "person", "toy">'.(2345)
16 Replies
Unknown User
Unknown User•3mo ago
Message Not Public
Sign In & Join Server To View
mike
mikeOP•3mo ago
ok, ... 🙂 solid workaround in such a case those can be constants 🙂 of course not with conditional if statements
Unknown User
Unknown User•3mo ago
Message Not Public
Sign In & Join Server To View
mike
mikeOP•3mo ago
So conditional builder would look like this? is this the best way how to do it? https://kyse.link/toaW0
koskimas
koskimas•3mo ago
Variable types can never become wider in typescript. When you join a table, the new query builder's type is wider (contains the information about the joined table). When you assign it to the old variable, the old variable's type doesn't (and can't) change. So you need to assign the result of a join to a new variable if you want to keep the new wider type. There's no way around it.
So conditional builder would look like this? is this the best way how to do it?
That doesn't work either. The type of the resulting query builder is the same as the original one.
mike
mikeOP•3mo ago
testing https://kysely.dev/docs/recipes/deduplicate-joins now if does (hopefully) what i need
Deduplicate joins | Kysely
When building dynamic queries, you sometimes end up in situations where the same join
Igal (mobile)
Igal (mobile)•3mo ago
There might be a way to widen variable types. A noop (arg: unknown): asserts arg is WiderType function call. Needs investigation for the generic case.
koskimas
koskimas•3mo ago
Nope, it just narrows it
No description
koskimas
koskimas•3mo ago
A better example
No description
Igal (mobile)
Igal (mobile)•3mo ago
I have an example somewhere in which it is not the case. where you can pretty much decorate an object with extra properties
koskimas
koskimas•3mo ago
An object with more properties is narrower A more specific interface. Less options fit it & narrows | widens That's what happens in my first example as well
Igal (mobile)
Igal (mobile)•3mo ago
you can also apply a union..
koskimas
koskimas•2mo ago
If you apply a union, it will narrow the type with the union. It doesn't union anything to the original type.
koskimas
koskimas•2mo ago
No description
koskimas
koskimas•2mo ago
asserts x is T will always just set (typeof x) & T as the type
Igal
Igal•2mo ago
Sorry, confused the lingo. Anyway, This is possible:
import { Selectable, SelectQueryBuilder } from 'kysely'

interface DB {
person: {
id: string;
}
pet: {
id: string;
owner_id: string | null;
}
}

let thing = null as unknown as SelectQueryBuilder<DB, 'person', Selectable<DB['person']>>

assertUnion(thing)

thing
// ^? SelectQueryBuilder<DB, "person" | "pet", ...>

function assertUnion(_x: unknown): asserts _x is SelectQueryBuilder<DB, 'pet' | 'person', Selectable<DB['person']> & Selectable<DB['pet']>> {
// noop
}
import { Selectable, SelectQueryBuilder } from 'kysely'

interface DB {
person: {
id: string;
}
pet: {
id: string;
owner_id: string | null;
}
}

let thing = null as unknown as SelectQueryBuilder<DB, 'person', Selectable<DB['person']>>

assertUnion(thing)

thing
// ^? SelectQueryBuilder<DB, "person" | "pet", ...>

function assertUnion(_x: unknown): asserts _x is SelectQueryBuilder<DB, 'pet' | 'person', Selectable<DB['person']> & Selectable<DB['pet']>> {
// noop
}
https://tsplay.dev/wE9Q3w It's wider in the sense that, there's a wider query context now.

Did you find this page helpful?