I am facing an issue with extending a Javascript ES6+ class that returns an object from the constructor.
Consider the following scenario:
class Base {
constructor() {
return {name: "base"}
}
}
class Extension extends Base {
constructor() {
super()
}
saySomething() {
console.log("What do you want me to say?")
}
}
When creating a new instance of the Extension
class and calling saySomething
, it fails with an error. This issue arises because, for some reason related to the base class returning a value in its constructor, the method saySomething
does not seem to exist on the Extension
class prototype (only properties from the base class are present).
The root cause of this problem stemmed from my attempt to extend a PrismaClient
as explained here. This led to certain methods on the PrismaService
class not being available on the prototype chain.
function PrismaExtended() {
type Params = ConstructorParameters<typeof PrismaClient<Prisma.PrismaClientOptions, Prisma.LogLevel>>;
const client = (...args: Params) =>
new PrismaClient<Prisma.PrismaClientOptions, Prisma.LogLevel>(...args).$extends({
result: {
$allModels: {
toJSON: {
compute(data: BaseEntity) {
return <T extends typeof data = typeof data>() => {
return Object.assign({}, data, {
sn: typeof data.sn === 'bigint' ? data.sn.toString() : data.sn,
}) as unknown as Omit<T, 'sn'> & { sn: string };
};
},
},
},
},
});
class PrismaExtended {
constructor(...args: Params) {
return client(...args);
}
}
// Object.setPrototypeOf(PrismaExtended, PrismaClient.prototype);
return PrismaExtended as new (...args: Params) => ReturnType<typeof client>;
}
@Injectable()
export class PrismaService extends PrismaExtended() implements OnModuleInit, OnModuleDestroy {
private retries: number = 0;
constructor(
private readonly logger: AppLogger,
configService: ConfigService,
) {
super({
log: pickFrom(configService, 'app.status') !== NodeEnv.PROD ? ['query'] : undefined,
transactionOptions: { timeout: 60_000 },
});
}
/**
* Check if an error is as a result of a precodition necessary to run an
* operation not being matched.
*
* @param e The error to check
* @returns boolean
*/
isUnmatchedPrecondition(e: unknown): e is PrismaClientKnownRequestError {
return e instanceof PrismaClientKnownRequestError && e.code === PrismaErrorCode.UNMATCHED_PRECONDITION;
}
}
In this context, the method isUnmatchedPrecondition
and any other additional methods do not exist at runtime.