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