Hello! I'm currently reworking some database code with new tooling, and I wanted a better structure for injection. Right now, I'm using a static class singleton with an asyncronous init method. However, I'm running into an issue trying to apply the class when I inject it.
The singleton:
// drizzle.db.ts
import { NodePgDatabase, drizzle } from 'drizzle-orm/node-postgres';
import pg from 'pg';
import * as schema from '../drizzle/schema.js';
export type dbType = NodePgDatabase<typeof schema>;
export class DrizzleDb {
static db: dbType;
static schema = schema;
static isInit = false;
static async init(connectionString: string) {
//don't double init
if (this.isInit) return;
this.isInit = true;
const client = new pg.Pool({
connectionString,
});
await client.connect();
this.db = drizzle(client, { schema });
}
}
All well and good up through there. This works so far. Here's the container for my ORM that takes a DrizzleDb as a parameter:
// Orm.ts
import { NodePgDatabase } from 'drizzle-orm/node-postgres';
import { DrizzleDb } from '../../drizzle.db.js';
export class Orm {
constructor(private dbService: typeof DrizzleDb) {}
public get schema() {
return this.dbService.schema;
}
public get db() {
return this.dbService.db;
}
}
This seems fine. But the signs of the upcoming error are present when I look at the type of the dbservice properties. The type of this.dbService.schema is DrizzleDb.schema: typeof import("/Users/kobold/Documents/kobold/src/drizzle/schema")
Here's where the issue comes in:
// character.model.ts
import { Orm } from '../Orm.js';
import { zCharacter } from './character.zod.js';
import 'drizzle-orm/pg-core';
export class dCharacter {
public z: typeof zCharacter;
// public query:
constructor(private orm: Orm) {
this.z = zCharacter;
}
public get query() {
return this.orm.db.query.character;
}
}
Here, I get an error on the type of public get query()
The inferred type of 'query' cannot be named without a reference to '../../../../../node_modules/drizzle-orm/pg-core/query-builders/query.js'. This is likely not portable. A type annotation is necessary.
I assume that this is because I'm hoisting an "imported type" through the Orm. I imagine it calls back to the Orm's typing of typeof DrizzleDb for the dependency in its constructor. However, I'm not entirely sure how to type that better. Is there some typescript trick here I'm not used to? Is this just a problem of using a static class singleton? Is there a way to do an instantiated version that doesn't have a possible undefined parameter after instantiation and then the init call? I would have gone the non-static method if I was more confident in my types and the ability to call .db without an undefined check every time.