Vincit / objection.js

An SQL-friendly ORM for Node.js

Home Page:https://vincit.github.io/objection.js

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

Model and ts-mixer not bind globaly with Model.knex

br-jeff opened this issue · comments

I've already seen this pattern working in a project where the 'outDir' is specified in the tsconfig.json file, so I believe it's the way TypeScript compiles, but in this case i don't wanna create a outDir folder.

versions: {
"ts-mixer": "^6.0.3",
"knex": "^3.0.1",
"objection": "^3.1.2",
"pg": "^8.11.2",
"ts-node": "^10.9.1",
}

'use strict';

const Knex = require('knex');

import { Mixin } from "ts-mixer";
import { Model } from 'objection'


class UserEntity {
    id: string

    username: string

    firstName: string

    lastName: string

    telephone: number

    password: string

    email: string

    createdAt: Date

    deletedAt: Date
}

class UserMixin extends Mixin(Model, UserEntity) {
    static get tableName() {
        return 'users'
    }
}

class UserNoMixin extends Model {
    id: string

    username: string

    firstName: string

    lastName: string

    telephone: number

    password: string

    email: string

    createdAt: Date

    deletedAt: Date

    static get tableName() {
        return 'users'
    }
}


const knex = Knex({
    client: 'sqlite3',
    useNullAsDefault: true,
    connection: {
        filename: 'example.db'
    }
});


async function main() {
    Model.knex(knex);

     console.log({ workingUserClass: await UserNoMixin.query() })
     console.log({ working: await UserMixin.bindKnex(knex).query() }) // working 
    console.log({ error: await UserMixin.query() }) // You need to bind the model class or the query to a knex instance
}

main()
    .then(() => knex.destroy())
    .catch((err) => {
        console.error(err);
        return knex.destroy();
    });

This way it's a working around

import { Model } from 'objection'
import { Mixin, settings } from "ts-mixer";
settings.staticsStrategy = 'proxy';

export class UserModel extends Mixin(UserEntity, Model) {
    static get tableName() {
        return 'users'
    }
}

can I suggest a different approach?

add these util types to your project

import { type NonFunctionPropertyNames } from 'objection'
type NonFunctionProperties<T> = Pick<T, NonFunctionPropertyNames<T>>
type IfEquals<X, Y, A=X, B=never> =
    (<T>() => T extends X ? 1 : 2) extends
    (<T>() => T extends Y ? 1 : 2) ? A : B
type NullableOptional<T> = { [P in keyof T]?: T[P] | null }
type ReadonlyKeysOf<T> = {
    [P in keyof T]-?: IfEquals<{ [Q in P]: T[P] }, { -readonly [Q in P]: T[P] }, never, P>
}[keyof T]
type OmitReadonly<T> = Omit<T, ReadonlyKeysOf<T>>

// these are what you'll using
export type EntityRead<T> = Omit<NonFunctionProperties<T>, 'QueryBuilderType'>
export type EntityInsert<T> = EntityRead<OmitReadonly<T>>
export type EntityUpdate<T> = NullableOptional<EntityInsert<T>>

use declare to describe model properties and also avoid the "no initializer" typescript warning

export class User extends Model {
    static tableName: string = 'users'

    declare public readonly id: string
    declare public username: string
    declare public password: string
    declare public optional?: string
    declare public readonly createdAt: string
    declare public readonly updatedAt: string

    public dance(): string {
        return '...'
    }
} // class

derive the entity interfaces from the model

export type UserEntityRead = EntityRead<User> // all props, no methods
export type UserEntityInsert = EntityInsert<User> // required and optional but no readonly
export type UserEntityUpdate = EntityUpdate<User> // no readonly, the rest optional and nullable