I am currently working on defining a utility function that will handle axios errors and store the resulting error message into a specific field of a specified object.
The desired syntax for using this function is:
axios.get(...).then(...).catch(ParseIntoErrorString(myClass, 'loadError');
In this code snippet, myClass
represents a TypeScript class containing a method that invokes this function (likely within a 'loadData' method). In most cases, myClass
may be replaced with 'this'.
The 'loadError' property is a string attribute within the myClass
. This concept is inspired by the behavior of jest.spyOn
, where you specify the target object to spy on and then indicate the function name to replace with a spy function. It's worth noting that jest.spyOn
triggers an error if a non-function string is provided.
Despite my efforts, I'm encountering issues with the following implementation:
type NonFunctionPropertyNames<T> = { [K in keyof T]: T[K] extends (...args: any[]) => any ? never : K }[keyof T] &
string;
export function ParseErrorIntoString<T extends {}, P extends NonFunctionPropertyNames<Required<T>>>(obj: T, member: P): (reason: AxiosError) => void {
return (reason: AxiosError): void => {
// eslint-disable-next-line no-param-reassign
obj[member] = AjaxParseError(reason);
};
}
The NonFunctionPropertyNames
portion has been directly extracted from the jest typings file.
Moreover, outside of failing at the calling site, this code raises two distinct errors:
- An error stating that one cannot use extends {} suggests replacing it with
Record<string, unknown>
. However, upon making this adjustment, passingthis
as an argument becomes problematic as it doesn't conform toRecord<string, unknown>
, though the reasoning isn't explicitly outlined. - There's an error when trying to assign a string to
obj[member]
, possibly due to the lack of restrictions withinNonFunctionPropertyNames
to allow only string properties.
I've also explored the following approach:
export function ParseErrorIntoString<T extends object>(obj: T, member: Extract<keyof T, string | null>): (reason: AxiosError) => void {
return (reason: AxiosError): void => {
// eslint-disable-next-line no-param-reassign
obj[member] = AjaxParseError(reason);
};
}
My assumption was that
Extract<keyof T, string | null>
would isolate keys within T bearing the type "string | null". Unfortunately, it proved to be too lenient, allowing boolean members without triggering an error when they were expected.
This laxness manifests during the assignment line obj[member] = ...
, manifesting as a type mismatch warning regarding assigning a string value to obj[member]
.
To make matters worse, explicit declaration of T is required at the calling site for proper member acceptance—an unexpected deviation from the behavior exhibited by spyOn
in jest.
Lastly, consider this self-contained example representing a third attempt nearing successful resolution:
class cls {
public str: string | null;
public bln: boolean;
constructor() {
this.str = "";
this.bln = false;
}
}
const instance = new cls();
type StringPropertyNames<T> = { [K in keyof T]: T[K] extends string | null ? K : never }[keyof T] & string;
function f<T extends cls>(obj: T, p: StringPropertyNames<T>): void {
obj[p] = "done";
}
f(instance, 'str'); // Calling f with the instance and property works fine
console.log(instance.str) // Outputs 'done'
In this scenario, f
mirrors the original ParseErrorIntoString
function. Notably, invoking f(instance, 'str')
produces the expected outcome, whereas attempting f(instance, 'bln')
correctly raises an error due to 'bln' being incompatible with the type string.
However, I'm still faced with the persistent error message: "Type 'string' is not assignable to type T[StringPropertyNames<T>]
" at the point of obj[p] = 'done'
. Possibly, refining the return type of StringPropertyNames<T>
could resolve this issue.
If anyone can suggest appropriate typing for the ParseErrorIntoString
function, it would be greatly appreciated!