typescriptkysely

How to cast query results to generated types?


I have my types generated for pg database via kysley-codegen and it generates this interface for my QuestionReview table:

export interface QuestionReview {
  id: Generated<number>;          // this will be a problem
  idQuestion: number;
  idUserReviewed: number;
  tsCreated: Timestamp;           // this will be a problem
  tsModified: Timestamp;          // this will be a problem
  userModified: string;
}

Now, in my service class I'm inserting into this table and want to return the newly inserted entity as Promise<QuestionReview>. Here is the code:

const rv = await this.db
      .insertInto('questionReview')
      .values({
        idQuestion: idQuestion,
        idUserReviewed: idAppUser,
      } as Insertable<QuestionReview>)
      .returningAll()
      .executeTakeFirstOrThrow();

which returns the rv object with these JS types:

{
  id: number,
  ...
  tsCreated: Date,
  tsModified: Date
}

I can not just return this object because TS cannot cast:

I've ended up returning this rather humiliating object with brute-force casting:

    return {
      ...rv,
      id: rv.id as unknown as Generated<number>,
      tsCreated: rv.tsCreated as unknown as Timestamp,
      tsModified: rv.tsModified as unknown as Timestamp,
    };

What am I doing wrong, is there a better way to return strongly typed inserted entity?


Solution

  • Use the use the Selectable, Insertable and Updateable wrappers.

    Consult the Kysely docs:

    // You should not use the table schema interfaces directly. Instead, you should
    // use the `Selectable`, `Insertable` and `Updateable` wrappers. These wrappers
    // make sure that the correct types are used in each operation.
    //
    // Most of the time you should trust the type inference and not use explicit
    // types at all. These types can be useful when typing function arguments.
    export type Person = Selectable<PersonTable>
    export type NewPerson = Insertable<PersonTable>
    export type PersonUpdate = Updateable<PersonTable>
    

    In your case:

    import {
      ColumnType,
      Generated,
      Insertable,
      JSONColumnType,
      Selectable,
      Updateable,
    } from 'kysely'
    
    // Generated
    export type Timestamp = ColumnType<Date, Date | string, Date | string>;
    
    // Generated
    export interface QuestionReview {
      id: Generated<number>;          // this will be a problem
      idQuestion: number;
      idUserReviewed: number;
      tsCreated: Timestamp;           // this will be a problem
      tsModified: Timestamp;          // this will be a problem
      userModified: string;
    }
    
    export type QuestionReviewSelectable = Selectable<QuestionReview>
    

    QuestionReviewSelectable is inferred to:

    type QuestionReviewSelectable = {
        id: number;
        idQuestion: number;
        idUserReviewed: number;
        tsCreated: Date;
        tsModified: Date;
        userModified: string;
    }
    

    TS Playground example