Skip to content

Instantly share code, notes, and snippets.

@wadefletch
Last active November 3, 2023 17:19
Show Gist options
  • Save wadefletch/668addf09e64c7bd162e87d270a4db0a to your computer and use it in GitHub Desktop.
Save wadefletch/668addf09e64c7bd162e87d270a4db0a to your computer and use it in GitHub Desktop.
import { eq } from "drizzle-orm";
import { type DrizzleD1Database } from "drizzle-orm/d1";
import { type GenericTable, project } from "./db/schema";
// This is what I'd like to use, which is defined with my schema.
// (See import above, though the rest of the file shouldn't be relevant.)
// type GenericTable = typeof <tableA> | typeof <tableB> | ...
// I also tried this, but it doesn't work either.
// const Tables = {
// project,
// apiKey, // another sqliteTable from ./db/schema.ts
// } as const;
// type GenericTable = (typeof Tables)[keyof typeof Tables];
// There isn't a type error when we avoid the union type and use a single table.
// type GenericTable = typeof project;
// Thanks for your help!
interface Reader<T extends GenericTable> {
// What's the difference between T["$inferSelect"] and InferSelectModel<T>?
findAll(): Promise<T["$inferSelect"][]>;
findOne(id: number): Promise<T["$inferSelect"] | undefined>;
}
export abstract class Repository<T extends GenericTable> implements Reader<T> {
constructor(
private readonly db: DrizzleD1Database,
private readonly table: T,
) {}
// 'T["$inferSelect"]' could be instantiated with an arbitrary type which could be unrelated to '{ [Key in keyof GetSelectTableSelection<T> & string]: SelectResultField<GetSelectTableSelection<T>[Key], true>; }'.
async findAll() {
return await this.db.select().from(this.table).all();
}
// 'T["$inferSelect"]' could be instantiated with an arbitrary type which could be unrelated to '{ [Key in keyof GetSelectTableSelection<T> & string]: SelectResultField<GetSelectTableSelection<T>[Key], true>; }'.
async findOne(id: number) {
return (await this.db.select().from(this.table).where(eq(this.table.id, id)).execute()).at(0);
}
}
// Would it make a difference to pass the table type, rather than a generic?
// export class ProjectRepository extends Repository<typeof project> {}
class ProjectRepository<T extends GenericTable> extends Repository<T> {}
// Dummy DB, since we're not actually running this code, just type checking.
const db = null as unknown as DrizzleD1Database;
const projectRepository = new ProjectRepository(db, project);
const proj = await projectRepository.findOne(1);
console.log(proj);
@Angelelz
Copy link

Angelelz commented Nov 3, 2023

Your problem is that your GenericTable is a union and Typescript is not sure if the $inferSelect is correct, because it could be coming from any of those tables in your union. You have to find the common stuff in your tables and make the rest generic. I'll see if I can put together an example in a typescript playground so you can play with it.

@Angelelz
Copy link

Angelelz commented Nov 3, 2023

I couldn't make it work with an union, any reason why you want it like that?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment