Skip to content

Instantly share code, notes, and snippets.

@NoriSte
Last active November 1, 2023 02:22
Show Gist options
  • Star 8 You must be signed in to star a gist
  • Fork 2 You must be signed in to fork a gist
  • Save NoriSte/936096262be7af6b6bd4cba7a71640d9 to your computer and use it in GitHub Desktop.
Save NoriSte/936096262be7af6b6bd4cba7a71640d9 to your computer and use it in GitHub Desktop.
Redux-saga + Typescript: implementing typeguard for Axios AJAX requests
import { AxiosResponse } from "axios";
// the kind of data I expect from the AJAX request...
interface AuthData {
access_token?: string;
refresh_token?: string;
}
// ... a type dedicated to the Axios response...
type AuthResponse = AxiosResponse<AuthData>;
// ... and the function that consumes Axios... (it's a part of my own function but the rest of the
// body isn't helpful for the sake of the Gist)
function postRefresh(refreshToken: string): Promise<AuthResponse> {
return new Promise(async (resolve) => {
const response: AuthResponse = response = await Axios.post(/* ... */);
resolve(response);
});
}
// the saga that consumes the AJAX request
function* UNSAFE_refreshAuthSaga() {
// ...
// Typescript can't check the return type of yielded value. If 'postRefresh' returns a different
// type of response Typescript can't check it and compiles without errors
const response: AuthResponse = yield call(postRefresh, "the_refresh_token");
// ...
}
function* SAFE_refreshAuthSaga() {
// ...
// That's a strong typeguard check, useful to work around the Typescript impossibility of checking
// a yielded function return value
// tslint:disable-next-line
let typeguard: Equals<Promise<AuthResponse>, ReturnType<typeof postRefresh>>;
const response: PromiseArgType<typeof typeguard> = yield call(postRefresh, "the_refresh_token");
// now if I change the return type of the "postRefresh" function Typescript raises an error
// ...
}
// some more Typescript utilities
type Equals<A, B> = [A] extends [B] ? ([B] extends [A] ? A : never) : never;
// @see https://stackoverflow.com/questions/48011353/how-to-unwrap-type-of-a-promise?rq=1
type PromiseArgType<T> = T extends Promise<infer U>
? U
: T extends (...args: any[]) => Promise<infer UU>
? UU
: T;
@heroneto
Copy link

Thansk, thats very usefull.

Above my implementaton using SagaReturnType, i think this is little bit simple:

import {
  login as loginService,
} from '@core/services/apis'


....
Some Code
....


type LoginServiceResponse = SagaReturnType<typeof loginService>

export function* login(action: Effect){
  try {
    const { username, password } = action.payload.data
    const result : LoginServiceResponse = yield call(loginService, username, password)


....
rest of code...

@NoriSte
Copy link
Author

NoriSte commented Feb 21, 2021

Thanks, I didn't know SagaReturnType! ❤️

@heroneto
Copy link

@NoriSte
Copy link
Author

NoriSte commented Feb 22, 2021

Thanks! Just a suggestion: in dev.to, instead of adding code comments through a triple backtick

```
const a = 1
```

suffix the opening backticks with the language (js or ts in your case)

```ts
const a = 1
```

to leverage code highlight 😉

The result is something like

const a = 1

instead of

const a = 1

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment