#How let typescript infer the type ?

26 messages · Page 1 of 1 (latest)

formal gull
#

I struggle always to express this kind of things in ts:

😢 any one have a tips or how write it ? I look up with "satisfies" (that i want the return type of qb satisfies "SelectQueryBuilder<DB, any, any>;" but keep the output type)

Thanks by advance ❤️

sly edgeBOT
#
totomakers#0

Preview:```ts
type SelectQueryBuilder<A, B, C> = any

interface DefineBlockAnalyticParams<DB> {
qb: (db: DB) => SelectQueryBuilder<DB, any, any>;
transform(data: Awaited<ReturnType<this['qb']>>['execute']): any;
}

const defineBlockAnalytic = <DB>(
params: DefineBlockAnalyticParams<DB>,
...```

#
andjsrk#0

Preview:```ts
type SelectQueryBuilder<A, B, C> = {
execute: B
}

interface DefineBlockAnalyticParams<
DB,
QbRes extends SelectQueryBuilder<DB, any, any>

{
qb: (db: DB) => QbRes
transform(data: Awaited<QbRes>["execute"]): any
}

const defineBlockAnalytic = <
DB,
QbRes extends SelectQueryBuilder<DB, any, any>
...```

potent valve
#

if you don't mind the function and the interface DefineBlockAnalyticParams accepting one more generic parameter, you can write a code like this

formal gull
#

Yeah i would like avoid doing it.

export const AvgVolumeForPeriodAnalytic =
  defineBlockAnalytic<TripDestinationSchema>({
    qb: (db) => {
      const qb = db
        .selectFrom('trip_point')
        .innerJoin('trip', 'trip.id', 'trip_point.trip_id')
        .innerJoin('trip_info', 'trip_info.id', 'trip.id')
        .innerJoin(
          'trip_point_info',
          'trip_point_info.from_id',
          'trip_point.id',
        )
        .select((eb) =>
          sql<number>`DATE_TRUNC('day', ${eb.ref('trip_point.timestamp')})`.as(
            'day',
          ),
        )
        .select(({ fn }) => fn.countAll<number>().as('point_count'))
        .select('vehicle_type')
        .groupBy(['trip.vehicle_type', 'day'])
        .orderBy('day', 'asc');

      return qb;
    },

    transform: (data) => {},
  });

That the output i woudl like write, qb is a kysely query builder and the output is qutie complexe.

#

Currently how i trick it is type the db of QB instead of passing it to type:


export const AvgVolumeForPeriodAnalytic = defineBlockAnalytic({
  qb: (db: Kysely<TripDestinationSchema>) => {
    const qb = db
      .selectFrom('trip_point')
      .innerJoin('trip', 'trip.id', 'trip_point.trip_id')
      .innerJoin('trip_info', 'trip_info.id', 'trip.id')
      .innerJoin('trip_point_info', 'trip_point_info.from_id', 'trip_point.id')
      .select((eb) =>
        sql<number>`DATE_TRUNC('day', ${eb.ref('trip_point.timestamp')})`.as(
          'day',
        ),
      )
      .select(({ fn }) => fn.countAll<number>().as('point_count'))
      .select('vehicle_type')
      .groupBy(['trip.vehicle_type', 'day'])
      .orderBy('day', 'asc');

    return qb;
  },

  transform: (data) => {},
});

do the trick but maybe a way exist to improve ?

potent valve
#

doesn't the query builder (library?) have an interface that contains execute? i guess there is an interface for that and has a generic parameter for execute...

potent valve
# sly edge

if there is one, i would edit the code above like this:

sly edgeBOT
#
andjsrk#0

Preview:```ts
type HasExecute<T> = {
// the interface that have execute
execute: T
}

interface DefineBlockAnalyticParams<DB, Res> {
qb: (db: DB) => HasExecute<Res>
transform(data: Res): any
}

const defineBlockAnalytic = <DB, Res>(
params: DefineBlockAnalyticParams<DB, Re
...```

formal gull
#

Yep cant really do that :/
Don't know how say it properly, but i would like restrict the type of the output but let ts do their job. Like "satisfies" key work like:

"i would like an object, with 2 parameters, qb, that should be a function that take "db", an instance of "Kysely<DB>" and any params, and transform that take the result of "QB" and return anything"

export interface DefineBlockAnalyticParams<DB> {
  qb: (db: Kysely<DB>, ...params: any) => SelectQueryBuilder<DB, any, any>;
  transform(data: Awaited<ReturnType<ReturnType<this['qb']>['execute']>>): any;
}

But if i do that TS don't infer anymore qb & transform and "force" the type to what i have defined, but it's more a constraint that a real output.
I try "satisfies" and this kind of things but no success T..T

potent valve
#

for declarations(e.g. const foo = {...}), yeah, you are right; satisfies is the solution. but for function arguments, the solution is generics.

#

generics do exactly what you want, why you don't wanna apply it?

formal gull
#

I have do that:

import { Kysely, SelectQueryBuilder } from 'kysely';

export interface DefineBlockAnalyticParams<
  DB,
  QB extends SelectQueryBuilder<DB, any, any>,
  O,
> {
  qb: (db: Kysely<DB>, ...params: any) => QB;
  transform(data: Awaited<ReturnType<ReturnType<this['qb']>['execute']>>): O;
}

export function defineBlockAnalytic<
  DB,
  QB extends SelectQueryBuilder<DB, any, any>,
  O,
>(params: DefineBlockAnalyticParams<DB, QB, O>) {
  return params;
}
#

And work well for qb & transform

#

but i loose the output (O)

potent valve
#

does transform return the proper type on declaration part?

formal gull
#

Getting unknow but

#
import { Kysely, SelectQueryBuilder } from 'kysely';

export interface DefineBlockAnalyticParams<
  DB,
  QB extends SelectQueryBuilder<DB, any, any>,
  QBParams extends any[],
  O,
> {
  qb: (db: Kysely<DB>, ...params: QBParams) => QB;
  transform: (
    data: Awaited<ReturnType<ReturnType<this['qb']>['execute']>>,
  ) => O;
}

export function defineBlockAnalytic<
  DB,
  QB extends SelectQueryBuilder<DB, any, any>,
  QBParams extends any[],
  O,
>(params: DefineBlockAnalyticParams<DB, QB, QBParams, O>) {
  return params;
}

This is the updated type i use

#

Look liks is because Awaited<ReturnType<ReturnType<this['qb']>['execute']>> is ambigous

#
import { Kysely, SelectQueryBuilder } from 'kysely';

export interface DefineBlockAnalyticParams<
  DB,
  QB extends SelectQueryBuilder<DB, any, any>,
  QBParams extends any[],
  QBResult extends Awaited<
    ReturnType<SelectQueryBuilder<DB, any, any>['execute']>
  >,
  O,
> {
  qb: (db: Kysely<DB>, ...params: QBParams) => QB;
  transform: (data: QBResult) => O;
}

export function defineBlockAnalytic<
  DB,
  QB extends SelectQueryBuilder<DB, any, any>,
  R extends Awaited<ReturnType<QB['execute']>>,
  QBParams extends any[],
  O,
>(params: DefineBlockAnalyticParams<DB, QB, QBParams, R, O>) {
  return params;
}

Fixed type 🥲

potent valve
#

wait, you can write just QB instead of ReturnType<this['qb']>

formal gull
#
import { Kysely, SelectQueryBuilder } from 'kysely';

export interface DefineBlockAnalyticParams<
  DB,
  QB extends SelectQueryBuilder<DB, any, any>,
  QBParams extends any[],
  QBResult extends Awaited<ReturnType<QB['execute']>>,
  O,
> {
  qb: (db: Kysely<DB>, ...params: QBParams) => QB;
  transform: (data: QBResult) => O;
}

export function defineBlockAnalytic<
  DB,
  QB extends SelectQueryBuilder<DB, any, any>,
  QBResult extends Awaited<ReturnType<QB['execute']>>,
  QBParams extends any[],
  O,
>(params: DefineBlockAnalyticParams<DB, QB, QBParams, QBResult, O>) {
  return params;
}
potent valve
#

and QBResult is not necessary to be a generic parameter, just inline it

formal gull
#

Oh thanks !

#

Perferct @potent valve thanks for your help \o/ more clear now in my mind about generic