Type of client extended with dynamic extensions

Hi! In Prisma docs they say that to get type of extended client we can use factory function and ReturnType utility as follows:
function getExtendedClient() {
return new PrismaClient().$extends({
/* extension */
})
}

type ExtendedPrismaClient = ReturnType<typeof getExtendedClient>
function getExtendedClient() {
return new PrismaClient().$extends({
/* extension */
})
}

type ExtendedPrismaClient = ReturnType<typeof getExtendedClient>
And this work fine but how I can get client type when the set of extensions is dynamic? I mean the case when extensions are provided as argument to factory function and it cannot by tell explicitly what extensions are ther?
function getExtendedClient(extensions: NeedType[] = []) { // missing type for extension
return extensions.reduce((client, extension) => client.$extends(extension), new PrismaClient());
}

type ExtendedPrismaClient = ReturnType<typeof getExtendedClient> // won't work
function getExtendedClient(extensions: NeedType[] = []) { // missing type for extension
return extensions.reduce((client, extension) => client.$extends(extension), new PrismaClient());
}

type ExtendedPrismaClient = ReturnType<typeof getExtendedClient> // won't work
Is this possible to handle such a case with Prisma and TypeScript?
2 Replies
RaphaelEtim
RaphaelEtim2w ago
Hi @Tomasz Kisiel I'm checking in with the team to see if this is possible. I'll provide a response as soon as i have one. Hi @Tomasz Kisiel Types are static by definition, they can’t be determined dynamically at run time.If the list of extensions you’re applying to the Prisma Client is truly dynamic and only known at runtime, it’s not possible to determine the resulting type. However, if the extensions are known statically (even if you’re applying them programmatically in code), you can create a utility to compute the extended client type. To do this, you would need to implement a type-level version of the reduce logic to fold the extensions into the base client type.
Tomasz Kisiel
Tomasz KisielOP2w ago
Hi @RaphaelEtim, thank you for your answer. Unfortunately what I want to achieve is the first case. But if it is not possible would it be at least possible to know extended client type without creating an instance of it? Let's say in the following case TypeScript know what is the type of prisma , but can I somehow get this type without creating isntance?
const xprisma = new PrismaClient().$extends(ext1).$extends(ext2).$extends(ext3)
type XPrisma = ???
const xprisma = new PrismaClient().$extends(ext1).$extends(ext2).$extends(ext3)
type XPrisma = ???
Okay, I think I’ve solved my problem with overriding an interface exported from a package where I want to keep the Prisma configuration and tools. Now, in the package, I have everything set up. In the application’s source code, whenever I want to use some extensions, I create a factory function and extend the interface exported from package with return type of this function. It is something like this:
import { PRISMA_CONFIG_KEY, PrismaConfig } from '@app/config/prisma.config';
import { PrismaModule, PrismaService, withExists, withQuery } from '@some-package/prisma';
import { Module } from '@nestjs/common';
import { PrismaClient } from '@prisma/client';

const extendPrismaClient = (client: PrismaClient) => {
return client.$extends(withExists).$extends(withQuery);
}

declare module "@some-package/prisma" {
export interface PrismaService extends ReturnType<typeof extendPrismaClient> { }
}

@Module({
imports: [
PrismaModule.forRootAsync({
global: true,
inject: [PRISMA_CONFIG_KEY],
useFactory: (prismaConfig: PrismaConfig) => extendPrismaClient(new PrismaService(prismaConfig))
}),
],
})
export class AppPrismaModule { }
import { PRISMA_CONFIG_KEY, PrismaConfig } from '@app/config/prisma.config';
import { PrismaModule, PrismaService, withExists, withQuery } from '@some-package/prisma';
import { Module } from '@nestjs/common';
import { PrismaClient } from '@prisma/client';

const extendPrismaClient = (client: PrismaClient) => {
return client.$extends(withExists).$extends(withQuery);
}

declare module "@some-package/prisma" {
export interface PrismaService extends ReturnType<typeof extendPrismaClient> { }
}

@Module({
imports: [
PrismaModule.forRootAsync({
global: true,
inject: [PRISMA_CONFIG_KEY],
useFactory: (prismaConfig: PrismaConfig) => extendPrismaClient(new PrismaService(prismaConfig))
}),
],
})
export class AppPrismaModule { }
Maybe it’s not the best solution, but it does its job.

Did you find this page helpful?