Redux Toolkit Structure with TS I've just taken the leap over to RTK instead of 'vanilla' redux and am blown away by how much less code I need to write. I'm not entirely sure how to structure my slices, however, and figured I'd ask for a small code review so I do not accidentally start breaking conventions from the start. This is how one of my slices is currently looking:
import { createAsyncThunk, createSlice, PayloadAction } from '@reduxjs/toolkit';
import bundle from '../../bundler';
import {BundleStartAction, BundleCompleteAction, BundlesState } from './bundleSliceInterface.ts'


export const createBundle = createAsyncThunk(
  'bundle',
  async (data: { cellId: string; input: string }, thunkAPI) =&gt; {
    const { cellId, input } = data;

    thunkAPI.dispatch(bundleStart({ cellId }));
    const result = await bundle(input);
    thunkAPI.dispatch(
      bundleComplete({ cellId, bundle: { code: result.code, err: result.err } })
    );
  }
);

const initialState: BundlesState = {};
const bundleSlice = createSlice({
  name: 'bundle',
  initialState,
  reducers: {
    bundleStart: (state, action: PayloadAction&lt;BundleStartAction&gt;) =&gt; {
      state[action.payload.cellId] = { loading: true, code: '', err: '' };
    },
    bundleComplete: (state, action: PayloadAction&lt;BundleCompleteAction&gt;) =&gt; {
      state[action.payload.cellId] = {
        loading: false,
        code: action.payload.bundle.code,
        err: action.payload.bundle.err,
      };
    },
  },
  extraReducers: {},
});

export const { bundleStart, bundleComplete } = bundleSlice.actions;
export default bundleSlice.reducer;

Any feedback or tips on how to structure slices properly is greatly appreciated.
Thanks in advance!
I am blown away by how much less code I need to write

I'm going to blow your mind even more because you actually do not need to dispatch any actions inside createAsyncThunk.  It does that for you!
The &quot;pending&quot; action doesn't have a payload so getting the cellId is a little bit tricky.  You can see the types of the dispatched actions in the docs.  The property that you want is action.meta.arg.cellId.
You get better TypeScript support on your reducer by using the builder callback notation.  It already knows the types of your thunk actions!
The part that I am unclear on is what your bundle function does as it seems like it catches errors on its own?  That's fine but you can also throw errors in the inner function of createAsyncThunk and handle them in the reducer with the createBundle.rejected action.  You can also use the rejectWithValue helper to customize the error payload.
export const createBundle = createAsyncThunk(
  &quot;bundle/create&quot;,
  async (data: { cellId: string; input: string }) =&gt; {
    const { cellId, input } = data;
    const result = await bundle(input);
    // this is the payload
    return {
      cellId,
      bundle: result
    };
  }
);

const initialState: BundlesState = {};
const bundleSlice = createSlice({
  name: &quot;bundle&quot;,
  initialState,
  reducers: {},
  extraReducers: (builder) =&gt;
    builder
      .addCase(createBundle.pending, (state, action) =&gt; {
        const { cellId } = action.meta.arg;
        state[cellId] = { loading: true, code: &quot;&quot;, err: &quot;&quot; };
      })
      .addCase(createBundle.rejected, (state, action) =&gt; {
        //??
      })
      .addCase(createBundle.fulfilled, (state, action) =&gt; {
        const { bundle, cellId } = action.payload;
        state[cellId] = {
          loading: false,
          code: bundle.code,
          err: bundle.err
        };
      })
});