I’m working to create a Prisma extension. For reasons, I started with JavaScript, using JSDoc hints for typing. The extension actually works just fine; however, the type hints are terrible, which led me to believe I was doing something wrong. In fact, when I switched to a typescript transpiler, all sorts of errors appeared in my declaration files that were invisible when using esbuild
. My biggest current challenge in squashing these is, I need to use a value provided by the user when the initial extension is instantiated as a key in various other type declarations in my declaration files.
So, I have
// file: index.d.ts
interface PGVectorInitArgs<M extends PrismaModelProps> {
modelName: M;
vectorFieldName: string;
idFieldName?: PrismaModelFields<M>;
}
and my main function
// file: now index.mts; was index.mjs
export const withPGVector = <M extends PrismaModelProps>(args: PGVectorInitArgs<M>) => // do stuff
with the expectation that the user would use it like
const prisma = new PrismaClient().$extends(withPGVector({
modelName: 'item',
vectorFieldName: 'embedding',
idFieldName: 'foo'
}));
then I’m going to want “embedding
” and “foo
” to be a valid keys in other definitions. For bonus points, I also want the type of the actual ID field, which I can get with a Prisma type utility if I know the key, but also needs to handle the default case where the user didn’t specify the optional idFieldName
, and it’s defaulting to id
.
What I have now, but isn’t working, is
export type idFieldKey<K, M extends PrismaModelProps> = K extends PGVectorInitArgs<M>['idFieldName']
? K extends undefined ? 'id' : `${idFieldName}` : never;
export type vectorFieldKey<K, M extends PrismaModelProps> = K extends PGVectorInitArgs<M>['vectorFieldName']
? `${vectorFieldName}` : never;
export type idFieldType<T, M extends PrismaModelProps, K extends idFieldKey<T, M>> = Pick<Prisma.Args<T, 'findUnique'>['where'], K>;
Immediately here I have the problem that ${idFieldName}
and ${vectorFieldName}
complain “Cannot find name ‘idFieldName'”. I get it, we’re in the declaration file, so it doesn’t have that. But furthermore, when I go to use it later, for example,
// file: overrides.d.ts
type extendedSelectArgs<TModel, Args, VFName> = {
[K in keyof Args]: Args[K] extends Prisma.Args<TModel, 'create'>['select']
? Args[K]
: never;
} & { [K in vectorFieldKey<VFName, TModel>]: { [K]: boolean }};
it complains of the K
in { [K]: boolean }
, “A computed property name in a type literal must refer to an expression whose type is a literal type or a ‘unique symbol’ type.ts(1170)
‘K’ only refers to a type, but is being used as a value here.ts(2693)”
That second error is making me think I need to do something like create a unique symbol in index.mts
, export it from there, import it into index.d.ts
and use it there, but even if I got that to work, it wouldn’t resolve the “Cannot find name ‘idFieldName'”.
Probably this would all be much, much easier if it were just native typescript and I wasn’t starting with these separated JavaScript / type definition files, but I really don’t want to re-write everything from scratch. Is there some way to handle this separation and use the user-defined variable in the type declaration files?