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

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

Open
br-jeff opened this issue Nov 7, 2023 · 2 comments
Open

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

br-jeff opened this issue Nov 7, 2023 · 2 comments

Comments

@br-jeff
Copy link

br-jeff commented Nov 7, 2023

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();
    });
@br-jeff
Copy link
Author

br-jeff commented Nov 7, 2023

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'
    }
}

@knicola
Copy link

knicola commented Dec 25, 2023

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

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants