From harness-claude
Types Redux state, actions, thunks, and hooks with full inference using createAsyncThunk, typed hooks, and builder callbacks. Helps fix type errors in RTK projects.
How this skill is triggered — by the user, by Claude, or both
Slash command
/harness-claude:redux-typescript-patternsThe summary Claude sees in its skill listing — used to decide when to auto-load this skill
> Type Redux state, actions, thunks, and hooks with full inference and minimal manual annotation
Type Redux state, actions, thunks, and hooks with full inference and minimal manual annotation
useDispatch and useSelectorextraReducers, createAsyncThunk, or middlewareRootState and AppDispatch from the store — never define them manually.useAppDispatch, useAppSelector) and use them everywhere.PayloadAction<T> for reducer parameter types. RTK infers the rest from createSlice.createAsyncThunk with three generic arguments: <ReturnType, ArgType, ThunkApiConfig>.extraReducers, use the builder callback — it provides full type inference for each case.isRejectedWithValue type guard for narrowing rejection payloads.as type assertions in reducers — if you need them, the type definition is wrong.// store/index.ts — inferred types
export type RootState = ReturnType<typeof store.getState>;
export type AppDispatch = typeof store.dispatch;
// Typed hooks — use throughout the app
export const useAppDispatch: () => AppDispatch = useDispatch;
export const useAppSelector: TypedUseSelectorHook<RootState> = useSelector;
// Typed createAsyncThunk
interface FetchError {
message: string;
code: number;
}
export const fetchUser = createAsyncThunk<
User, // Fulfilled return type
string, // Argument type (userId)
{ state: RootState; rejectValue: FetchError } // ThunkAPI config
>(
'users/fetch',
async (userId, { rejectWithValue }) => {
const res = await fetch(`/api/users/${userId}`);
if (!res.ok) {
return rejectWithValue({ message: 'Not found', code: res.status });
}
return res.json();
}
);
// extraReducers with full inference
extraReducers: (builder) => {
builder
.addCase(fetchUser.fulfilled, (state, action) => {
// action.payload is User (inferred)
state.currentUser = action.payload;
})
.addCase(fetchUser.rejected, (state, action) => {
if (action.payload) {
// action.payload is FetchError (from rejectWithValue)
state.error = action.payload.message;
} else {
// action.error is SerializedError (thrown exception)
state.error = action.error.message ?? 'Unknown error';
}
});
},
ThunkAPI config object: Pass { state: RootState; rejectValue: T; dispatch: AppDispatch } as the third generic to createAsyncThunk. This types getState(), rejectWithValue(), and dispatch() inside the payload creator.
Middleware typing: Custom middleware receives MiddlewareAPI<AppDispatch, RootState>:
const logger: Middleware<{}, RootState> = (storeApi) => (next) => (action) => {
console.log('dispatching', action);
return next(action);
};
Entity adapter typing: Pass the entity type to createEntityAdapter<User>(). The adapter methods and selectors are fully typed from this.
Common type errors and fixes:
Property does not exist on type in getState() — add state: RootState to the thunk configArgument not assignable to PayloadAction — check that the action creator's prepare callback return type matchesType instantiation is excessively deep — usually from circular slice imports; break the cycle with a shared types fileCannot use namespace as type — import types with import type to avoid circular dependenciesRTK 2.0 changes: Tuple replaces arrays for middleware. combineSlices provides automatic type inference for lazy-loaded slices. The reducer field in configureStore accepts a combineSlices result directly.
https://redux-toolkit.js.org/usage/usage-with-typescript
npx claudepluginhub intense-visions/harness-engineering --plugin harness-claudeSets up Redux store using configureStore, typed hooks, middleware, and Provider. Useful for initializing Redux in React apps or migrating from legacy createStore.
Guides type-safe Zustand stores in TypeScript: interfaces, typed selectors, getters, async actions, type inference, and best practices.
Creates Zustand stores with TypeScript types, subscribeWithSelector middleware, separated state/actions, individual selectors, and non-React subscriptions. For React state management.