How should you type a async thunk being passed as an argument?

Viewed 148

Given the example below, I'm having a hard time figuring out what the loadAction argument should be typed as when passed to connect.

interface ApiResponse {
  data: string;
}

interface ApiErrorResponse {
  message: string;
}

const api = () =>
  new Promise<ApiResponse>((resolve) => {
    resolve({ data: "123" });
  });

const loadSomething = createAsyncThunk<
  string,
  void,
  { rejectValue: ApiErrorResponse }
>("something", async (_, { rejectWithValue }) => {
  try {
    const result = await api();
    return result.data;
  } catch (err) {
    if (!err.response) throw err;

    return rejectWithValue(err.data as ApiErrorResponse);
  }
});

const List = ({ data = "Something", loadAction }: ListProps) => {
  React.useEffect(() => {
    const fetchData = async () => {
      const result = await loadAction();
      if (result.type.endsWith("rejected")) alert("Failed");
    };
    fetchData();
  }, [loadAction]);

  return <div>#{data}</div>;
};

interface DispatchProps {
  loadAction: () => WhatTypeAmI?
}

interface OwnProps {
  data?: string;
}

type ListProps = DispatchProps & OwnProps;

const mapDispatch = {
  loadAction: loadSomething
};

const ListContainer = connect<void, DispatchProps, OwnProps, RootState>(
  null,
  mapDispatch
)(List);

I've tried:

AsyncThunkAction<string, void, {}> but then the compiler complains that result (inside my useEffect hook) doesn't have a type property.

Promise<PayloadAction<object>> but the compiler indicates that loadSomething is not compatible.

What's the proper type for loadAction that would satisfy all the requirements?

1 Answers

Instead of manually typing connect you can use the ConnectedProps helper to infer the types:

const mapDispatch = {
  loadAction: loadSomething,
};

const connector = connect(null, mapDispatch);

type PropsFromRedux = ConnectedProps<typeof connector>;

interface ListProps extends PropsFromRedux {
  data?: string;
}

const List = ({ data = 'Something', loadAction }: ListProps) => {
  React.useEffect(() => {
    const fetchData = async () => {
      const result = await loadAction();
      if (result.type.endsWith('rejected')) alert('Failed');
    };
    fetchData();
  }, [loadAction]);

  return <div>#{data}</div>;
};

const ListContainer = connector(List);

Reference: Inferring The Connected Props Automatically

Related