Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

I'm receiving a type error when using two async actions on one reducer #233

Closed
2 tasks done
brunoti opened this issue Jun 9, 2020 · 3 comments
Closed
2 tasks done
Assignees

Comments

@brunoti
Copy link

brunoti commented Jun 9, 2020

Description

I'm receiving a type error when using two async actions on one reducer:

Argument of type '(response: AxiosResponse<LoginResponse>) => PayloadAction<"USER/AUTH/RENEW_SUCCESS", AxiosResponse<LoginResponse>>' is not assignable to parameter of type '((...args: any[]) => PayloadAction<"USER/PERMISSION/SET_LOCATION", PermissionInfo> | PayloadAction<"USER/PERMISSION/SET_LOADING", boolean> | ... 11 more ... | EmptyAction<...>) | ((...args: any[]) => PayloadAction<...> | ... 12 more ... | EmptyAction<...>)[]'.
  Type '(response: AxiosResponse<LoginResponse>) => PayloadAction<"USER/AUTH/RENEW_SUCCESS", AxiosResponse<LoginResponse>>' is not assignable to type '(...args: any[]) => PayloadAction<"USER/PERMISSION/SET_LOCATION", PermissionInfo> | PayloadAction<"USER/PERMISSION/SET_LOADING", boolean> | ... 11 more ... | EmptyAction<...>'.
    Type 'PayloadAction<"USER/AUTH/RENEW_SUCCESS", AxiosResponse<LoginResponse>>' is not assignable to type 'PayloadAction<"USER/PERMISSION/SET_LOCATION", PermissionInfo> | PayloadAction<"USER/PERMISSION/SET_LOADING", boolean> | ... 11 more ... | EmptyAction<...>'.
      Type 'PayloadAction<"USER/AUTH/RENEW_SUCCESS", AxiosResponse<LoginResponse>>' is not assignable to type 'PayloadAction<"USER/PERMISSION/SET_LOCATION", PermissionInfo>'.
        Type '"USER/AUTH/RENEW_SUCCESS"' is not assignable to type '"USER/PERMISSION/SET_LOCATION"'.

Mandatory info

  • Are you extending internal types to enable type-free syntax with createReducer?
import { StateType, ActionType } from 'typesafe-actions';

declare module 'typesafe-actions' {
  export type Store = StateType<typeof import('./index').store>;
  export type RootState = StateType<typeof import('./root-reducer').reducer>;
  export type RootAction = ActionType<typeof import('./root-action')>;

  interface Types {
    RootAction: RootAction;
  }
}

How to Reproduce

This is my folder structure:

./app/store
├── clients.ts
├── index.ts
├── root-action.ts
├── root-reducer.ts
└── types.d.ts
./app/basket/store
├── actions.ts
├── order
│   ├── actions.ts
│   └── reducer.ts
├── product
│   ├── actions.ts
│   └── reducer.ts
├── reducer.ts
└── search
    ├── actions.ts
    └── reducer.ts
./app/user/store
├── actions.ts
├── auth
│   ├── actions.ts
│   ├── reducer.ts
│   └── thunks.ts
├── permission
│   ├── actions.ts
│   └── reducer.ts
└── reducer.ts
import { createReducer } from 'typesafe-actions';
import { UserResponse } from 'user/types';
import * as actions from './actions';

type UserAuthState = Readonly<{
  data?: UserResponse;
  loading: boolean;
  token?: string;
}>;

const INITIAL_STATE: UserAuthState = {
  data: undefined,
  token: undefined,
  loading: false,
};

const reducer = createReducer(INITIAL_STATE)
  .handleAction(actions.login.success, (state, { payload }) => ({
    ...state,
    data: payload.data.user,
    token: payload.data.token,
    loading: false,
  }))
  .handleAction(actions.login.request, state => ({
    ...state,
    loading: true,
  }))
  .handleAction(actions.login.failure, state => ({
    ...state,
    loading: false,
  }))
  .handleAction(actions.setUser, (state, { payload }) => ({
    ...state,
    data: payload,
  }))
  .handleAction(actions.setToken, (state, { payload }) => ({
    ...state,
    token: payload,
  }))
  .handleAction(actions.renew.success, (state, { payload }) => ({ // Here I receive the error descripted
    ...state,
    data: payload.data.user,
    token: payload.data.token,
  }))
  .handleAction(actions.reset, () => ({ ...INITIAL_STATE }));

export { reducer };

My async actions (./app/user/store/auth):

export const renew = createAsyncAction(
  ['USER/AUTH/RENEW', (): RequestPayload => ({
    client: 'user',
    request: {
      method: 'POST',
      url: '/renew',
    },
  })],
  ['USER/AUTH/RENEW_SUCCESS', (response: AxiosResponse<LoginResponse>): AxiosResponse<LoginResponse> => response],
  ['USER/AUTH/RENEW_FAIL', (error: AxiosError): AxiosError => error],
)<RequestPayload, AxiosResponse<LoginResponse>, AxiosError>();

export const renew = createAsyncAction(
  ['USER/AUTH/RENEW', (): RequestPayload => ({
    client: 'user',
    request: {
      method: 'POST',
      url: '/renew',
    },
  })],
  ['USER/AUTH/RENEW_SUCCESS', (response: AxiosResponse<LoginResponse>): AxiosResponse<LoginResponse> => response],
  ['USER/AUTH/RENEW_FAIL', (error: AxiosError): AxiosError => error],
)<RequestPayload, AxiosResponse<LoginResponse>, AxiosError>();

export const login = createAsyncAction(
  ['USER/AUTH/LOGIN', (data: Login): RequestPayload => ({
    client: 'user',
    request: {
      method: 'POST',
      url: '/login',
      data,
    },
  })],
  ['USER/AUTH/LOGIN_SUCCESS', (response: AxiosResponse<LoginResponse>): AxiosResponse<LoginResponse> => response],
  ['USER/AUTH/LOGIN_FAIL', (error: AxiosError): AxiosError => error],
)<RequestPayload, AxiosResponse<LoginResponse>, AxiosError>();

The action the error cites as conflict (./app/user/store/permissions/actions.ts):

import { createAction } from 'typesafe-actions';
import { PermissionInfo } from 'expo-permissions';

export const setLocation = createAction('USER/PERMISSION/SET_LOCATION')<PermissionInfo>();
export const setLoading = createAction('USER/PERMISSION/SET_LOADING')<boolean>();

Expected behavior

No type error there, since everything was ok until I added the renew handlers on this reducer.
I've tried a lot of stuff, changing types, removing that other action but the error just changes the message and never goes away.

Suggested solution(s)

I really don't know. But if I can help in any way, just say. I'm sorry I couldn't put a codesandbox link. I'm using ReactNative and I found it very hard to recreate the same environment on codesandbox.

Project Dependencies

  • Typesafe-Actions Version: 5.1.0
  • TypeScript Version: 3.8.3
  • tsconfig.json:
{
  "compilerOptions": {
    "jsx": "react-native",
    "lib": ["dom", "esnext"],
    "target": "esnext",
    "moduleResolution": "node",
    "allowSyntheticDefaultImports": true,
    "sourceMap": true,
    "removeComments": true,
    "strict": true,
    "noImplicitAny": true,
    "strictNullChecks": true,
    "strictFunctionTypes": true,
    "noImplicitThis": true,
    "alwaysStrict": true,
    "noUnusedLocals": true,
    "noUnusedParameters": true,
    "noImplicitReturns": true,
    "noFallthroughCasesInSwitch": true,
    "noEmit": true,
    "skipLibCheck": true,
    "incremental": true,
    "composite": true,
    "esModuleInterop": true,
    "emitDecoratorMetadata": true,
    "noEmitHelpers": true,
    "declaration": false,
    "importHelpers": true,
    "baseUrl": "app",
    "resolveJsonModule": true,
    "isolatedModules": true,
    "noEmitOnError": false,
    "pretty": true,
    "paths": {
      "*": ["*"]
    }
  },
  "include": [
    "app/**/*"
  ],
  "exclude": [
    "./node_modules/**/*"
  ]
}

Environment (optional)

  • Browser and Version: React Native / Expo 37
  • OS: MacOS Catalina
  • Node Version: 13
  • Package Manager and Version: Yarn 1.22.4
@piotrwitek piotrwitek self-assigned this Jun 9, 2020
@piotrwitek
Copy link
Owner

piotrwitek commented Jun 9, 2020

Hey @brunoti,

When declaring async actions there are 2 typing styles, please make sure to not use both of them. Here are example docs: #143

Basically try this:

export const renew = createAsyncAction(
  ['USER/AUTH/RENEW', (): RequestPayload => ({
    client: 'user',
    request: {
      method: 'POST',
      url: '/renew',
    },
  })],
  ['USER/AUTH/RENEW_SUCCESS', (response: AxiosResponse<LoginResponse>): AxiosResponse<LoginResponse> => response],
  ['USER/AUTH/RENEW_FAIL', (error: AxiosError): AxiosError => error],
)();

Please do the same with all async actions respectively.

@brunoti
Copy link
Author

brunoti commented Jun 10, 2020

That worked! I got confused between docs in the beginning. That plus tutorials got me to the wrong path.

@brunoti brunoti closed this as completed Jun 10, 2020
@piotrwitek
Copy link
Owner

Yes it's confusing right now I agree, I have to work on it.

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

No branches or pull requests

2 participants