I have been working on creating a wrapper for all my third party API interfaces and SDKs that logs requests in a standardized yet customizable way. My approach involves passing the third party API (typically instantiated with a new API()
call) into a wrapper class (APIClient
). This client receives an object with specific methods from the third party API mapped to logging functions, allowing me to specify actions such as sanitizing Personally Identifiable Information (PII). The process involves iterating over this object and redefining the methods defined on the third party API within the wrapper, triggering the logging function after invoking the third party method. This setup enables the API to maintain the same interface as the original API with added custom behavior.
Despite making progress towards getting this system to work, I have encountered challenges with typings that are proving difficult to overcome. Although inspired by TypeScript documentation on "Mixins," I am uncertain if that is the correct approach in this scenario.
Some of the error messages I'm facing are quite perplexing:
Type 'Function' does not match the signature '(...args: any): any'.
No index signature with a parameter of type 'string' was found on type 'ApiClient<T>'.
(The second error is somewhat clearer to me, as I understand that Object.entries presents key-value pairs as strings and values. However, I'm unsure about the next step.)
If anyone can spot what might be causing these issues and offer suggestions on how to resolve them effectively, I would greatly appreciate it. Thank you.
type Constructor = new (...args: any[]) => {};
type Method<T, K extends keyof T> = T[K] extends Function ? T[K] : never;
class ApiClient<T extends Constructor> {
_api: T;
constructor(api: T, logConfig: Record<keyof T, () => void>) {
this._api = api;
for (const [method, fn] of Object.entries(logConfig)) {
this[method] = this.createWrappedMethod(method, fn)
}
}
createWrappedMethod<
N extends keyof InstanceType<T>,
M extends Method<InstanceType<T>, N>,
>(name: N, logFn: () => void) {
return async (...args: Parameters<M>) => {
try {
const res = await this._api[name](...args);
// perform logging
} catch {
// handle errors`
}
};
}
}