我在扩展从构造函数返回对象的 Javascript ES6+ 类时遇到问题。
举个例子:
class Base {
constructor() {
return {name: "base"}
}
}
class Extension extends Base {
constructor() {
super()
}
saySomething() {
console.log("What do you want me to say?")
}
}
创建
Extension
类的新实例并调用 saySomething
失败并出现错误,因为无论出于何种原因,我认为这与基类在其构造函数中返回值有关,方法 saySomething
似乎不存在(事实上,除了属于基类的内容之外,什么也没有)在 Extension
类原型上。
导致这个基本问题的主要问题是我试图创建一个扩展的
PrismaClient
如此处所示,在 NestJS 中使用客户端扩展,这种特定方式会导致 PrismaService
类上的其他方法不存在在原型链上。
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;
}
}
这里的方法,
isUnmatchedPrecondition
,以及添加的任何其他方法在运行时都不存在。
我现在可以使用
Proxy
让它发挥作用。
使用双代理,一种用于在
PrismaService
上使用 new 关键字时拦截对象构造,并且因为理想情况下应该从 Proxy
继承的 PrismaClient
构造的对象没有“”的方法或属性假定的基类”(因为没有发生 PrismaClient
的继承),它已经在子类构造函数上下文之外构造,我必须保留“假定的基类”的引用并维护对构造的代理子类,这样当在子类实例上访问任何方法或属性并且找不到它时,它会被重定向到假定的基类,我在调用 getClient
的最外层代理中保留对该基类实例的引用。
function PrismaExtended() {
type Params = ConstructorParameters<typeof PrismaClient<Prisma.PrismaClientOptions, Prisma.LogLevel>>;
const getClient = (...args: Params) => {
return 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 };
};
},
},
},
},
});
};
return new Proxy(class {}, {
construct(target, args, newTarget) {
const client = getClient(...args);
const constructed = Reflect.construct(target, args, newTarget);
return new Proxy(constructed, {
get(target, key) {
if (key in target) return target[key];
return client[key];
},
});
},
}) as unknown as new (...args: Params) => ReturnType<typeof getClient>;
}
目前,这似乎工作正常,到目前为止我还没有记录任何“捕获”。但是,嘿,如果你确实注意到我遗漏了一些东西,或者关于这个解决方案的任何陷阱,请告诉我。
感谢所有为帮助我做出贡献的人。