Skip to content
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

Subqueries inside SELECT are not nullable #420

Closed
danielrearden opened this issue Apr 10, 2023 · 2 comments
Closed

Subqueries inside SELECT are not nullable #420

danielrearden opened this issue Apr 10, 2023 · 2 comments
Labels
breaking change Includes breaking changes enhancement New feature or request typescript Related to Typescript

Comments

@danielrearden
Copy link

danielrearden commented Apr 10, 2023

Example from the playground:

interface DB {
  user: User
}

interface User {
  id: string
  first_name: string | null
  last_name: string
}

const rows = await kysely
  .selectFrom('user')
  .select(['first_name', 'last_name', kysely.selectFrom('user').select('id').limit(1).as('some_id')])
  .execute()

Here, the resulting type is

{
  first_name: string | null;
  last_name: string;
  some_id: string;
}

However, there's no guarantees that the provided subquery will actually return any rows, and in the case where there are no rows returned, some_id would evaluate to null.

Is there a way to get string | null as the type for some_id (without using $castTo)? If not, I think it would make sense to either make these values nullable by default, or provide a way to configure this behavior either globally or an a per-query basis.

If the value was nullable by default, that would obviously be a pretty significant breaking change. However, it is arguably the safest approach, since the nullability of the value is contingent on not just the WHERE condition inside the subquery, but also the state of the database itself. The value could safely be made non-nullable by wrapping the whole expression inside a coalesce function. If there's a particular scenario where the value could never be null, then arguably that would be an appropriate use case for manually overriding the type -- and not the other way around. Just my two cents.

@igalklebanov igalklebanov added enhancement New feature or request typescript Related to Typescript labels Apr 10, 2023
@koskimas
Copy link
Member

That's a good point! I'll see if there is a clean way to make the type nullable in these cases.

@igalklebanov igalklebanov added the breaking change Includes breaking changes label Apr 10, 2023
@jirutka
Copy link

jirutka commented Dec 22, 2024

I have the opposite problem. My subquery can never return null and I cannot figure out how to get rid of | null in the return type without using an unnecessary coalesce. $castTo doesn’t work here.

.selectFrom('timetableSlot as s')
...
.select(eb => eb
  .selectFrom('timetableSlotTeacher as t')
  .select(eb => sql<number[]>`json_group_array(${eb.ref('t.teacherId')})`.as('x'))
  .whereRef('t.timetableSlotId', '=', 's.id')
  .$castTo<number[]>() // <-- has no effect
  .as('teacherIds'),
)
...
.execute()

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
breaking change Includes breaking changes enhancement New feature or request typescript Related to Typescript
Projects
None yet
Development

No branches or pull requests

4 participants