I have a class that houses my API code for my application:
export class Api {
...
static requestData = async (
abortController: React.MutableRefObject<AbortController | null>
) => {
// If previous request, cancel
if (abortController.current) {
abortController.current.abort();
}
// Set new controller for new request
abortController.current = new AbortController();
try {
const response = await instance.get(
"request/url/string",
{ signal: abortController.current.signal, },
);
if (response.success) {
// Handle successful response
} else {
// Handle known error
}
} catch (err: unknown) {
const signal = abortController.current.signal;
if (abortController.current.signal.aborted) {
// Handle aborted request
} else {
// Handle unknown error
}
}
};
The Axios instance is defined earlier, using axios.create
const instance = axios.create({
baseURL: "base-url-path",
});
I want to refactor the code that aborts and creates the new AbortController
// If previous request, cancel
if (abortController.current) {
abortController.current.abort();
}
// Set new controller for new request
abortController.current = new AbortController();
into its own function (to be reused elsewhere and reduce the amount of repeated code).
Attempt 1
I tried to move the above code to its own function inside the class:
public static abortPreviousAndSetUpNewController = (
abortController: React.MutableRefObject<AbortController | null>
) => {
// If previous request, cancel
if (abortController.current) {
abortController.current.abort();
}
// Set new controller for new request
abortController.current = new AbortController();
};
So that I could do the following:
static requestData = async (
abortController: React.MutableRefObject<AbortController | null>
) => {
this.abortPreviousAndSetUpNewController();
try {
const response = await instance.get(
"request/url/string",
{ signal: abortController.current.signal, },
);
if (response.success) {
// Handle successful response
} else {
// Handle known error
}
} catch (err: unknown) {
const signal = abortController.current.signal;
if (abortController.current.signal.aborted) {
// Handle aborted request
} else {
// Handle unknown error
}
}
};
But I get the error: 'abortController.current' is possibly 'null'.ts(18047)
Attempt 2
I also tried to use the is
keyword
Example code that made me consider using is
:
export const notNullNorUndefined = <T = any>(
value: T
): value is NonNullable<T> => value !== null && value !== undefined;
If a potentially null
/undefined
object is passed to this function, if the result is true
the compiler accepts that the type of the object passed cannot be null | undefined
anymore.
Attempt 2 code:
public static abortPreviousAndSetUpNewController = (
abortController: React.MutableRefObject<AbortController | null>
): abortController is React.MutableRefObject<AbortController | null> => {
// If previous request, cancel
if (abortController.current) {
abortController.current.abort();
}
// Set new controller for new request
abortController.current = new AbortController();
}
But this returns syntax errors: A function whose declared type is neither 'undefined', 'void', nor 'any' must return a value.
How do I tell TypeScript that my function (abortPreviousAndSetUpNewController
) has mutated abortController.current
so that its' type is no longer AbortController | null
and only AbortController
?
I would prefer not to assert the type where possible
Note, my package versions are:
- axios - 0.24.0
- react - 16.14.0