无法在 createAsyncThunk 中将 getState 类型设置为 RootState

Cannot set getState type to RootState in createAsyncThunk

我无法将 getState() 的 return 类型设置为 RootState。我正在使用打字稿和 VSCode。我必须将类型设置为 any,这会停止对该对象的 IntelliSense。下面是有问题的代码:

export const unsubscribeMeta = createAsyncThunk(
  'meta/unsubscribe',
  async (_, { getState }) => {
    const { meta } = getState() as any;
    const res = await client.post<apiUnsubscribeResponse>(
      `/meta/unsubscribe/${meta.subscriptionId}`
    );
    return res.data.data;
  }
);

如果我尝试使用 RootState 而不是 any,模块中的许多错误会被 VSCode 标记。我相信这是由于对商店和这个切片的循环依赖。我在选择器模块中的许多地方使用 RootState,没有问题。有解决办法吗?

你真的不需要知道整个州的形状。您只需要知道您尝试访问的值是否存在。

如果您可以访问整个 state.meta 类型:

const { meta } = getState() as { meta: MetaState };

如果没有:

const { meta } = getState() as { meta: { subscriptionId: string } };

我推荐这种方法来避免循环依赖,因为根状态将始终依赖于切片,所以切片不应该依赖于根。

只需从您的 ThunkApiConfig 类型中省略 state: RootState,然后您就可以在您的 payloadCreator 中使用 const state = getState() as RootState; 而无需循环依赖。

createAsyncThunk 可以具有在泛型上定义的类型:

export const unsubscribeMeta = createAsyncThunk<apiUnsubscribeResponse, void, {state: RootState }>(
  'meta/unsubscribe',
  async (_, { getState }) => {
    const { meta } = getState();
    const res = await client.post<apiUnsubscribeResponse>(
      `/meta/unsubscribe/${meta.subscriptionId}`
    );
    return res.data.data;
  }
);

定义 state 将自动使 getState 了解应用程序状态。

您可以使用 Typescript 的 module augmentation 功能将默认状态分配给 AsyncThunkConfig.state,这将是我们稍后调用它时 getState() 的返回类型。

declare module "@reduxjs/toolkit" {
  type AsyncThunkConfig = {
    state?: unknown;
    dispatch?: Dispatch;
    extra?: unknown;
    rejectValue?: unknown;
    serializedErrorType?: unknown;
  };

  function createAsyncThunk<
    Returned,
    ThunkArg = void,
    ThunkApiConfig extends AsyncThunkConfig = {
      state: YourRootState; // this line makes a difference
    }
  >(
    typePrefix: string,
    payloadCreator: AsyncThunkPayloadCreator<
      Returned,
      ThunkArg,
      ThunkApiConfig
    >,
    options?: any
  ): AsyncThunk<Returned, ThunkArg, ThunkApiConfig>;
}

其中 YourRootState 是您的商店状态的类型。

type YourRootState = {
  myNumber: number;
  myString: string;
};

现在您可以像往常一样使用 createAsyncThunkgetState() returns 正确的类型。

const doSomethingAsync = createAsyncThunk(
  "mySlice/action",
  async (_, { getState, dispatch }) => {
    const rootState = getState(); // has type YourRootState

    console.log(rootState.myNumber);
    console.log(rootState.myString);
  }
);


function Child() {
  const dispatch = useDispatch();
  return <button onClick={() => dispatch(doSomethingAsync())}>Click</button>;
}

现场演示

我将继续 NearHuscarl 的回答,因为我无法建议对其进行修改。

NearHuscarl 的回答很好,但是问题是他将 options 类型设置为 any,因此它解决了一个问题,如果您在其中使用 options createAsyncThunk 您必须手动设置其所有类型,否则 typescript 将引发 Binding element implicitly has an 'any' type. 错误。

所以只需像下面这样设置 options 类型就可以解决这个问题。

declare module "@reduxjs/toolkit" {
    type AsyncThunkConfig = {
        state?: unknown;
        dispatch?: Dispatch;
        extra?: unknown;
        rejectValue?: unknown;
        serializedErrorType?: unknown;
    };

    function createAsyncThunk<
        Returned,
        ThunkArg = void,
        ThunkApiConfig extends AsyncThunkConfig = { state: RootState } // here is the magic line
    >(
        typePrefix: string,
        payloadCreator: AsyncThunkPayloadCreator<
            Returned,
            ThunkArg,
            ThunkApiConfig
        >,
        options?: AsyncThunkOptions<ThunkArg, ThunkApiConfig>,
    ): AsyncThunk<Returned, ThunkArg, ThunkApiConfig>;
}