When the reducers in a createSlice have mixed payload action types, I get weird type errors as of TypeScript 3.4.1 (Maybe 3.4.0 as well, I didn't check).

It's like it's assuming that all PayloadActions it sees should be the same type as the first one.
Code for reproduction:
interface MyState {
name: string;
loading: boolean;
count: number;
}
const INITIAL_STATE: MyState = {
name: '',
loading: true,
count: 0,
}
const slice = createSlice({
slice: 'mySlice',
initialState: INITIAL_STATE,
reducers: {
setCount : (state, action: PayloadAction<number>) => { state.count = action.payload },
setName: (state, action: PayloadAction<string>) => { state.name = action.payload },
setLoaded: (state) => { state.loading = false; },
}
})
I'm also having issues with createSlice types.
const slice = createSlice({
initialState: {
data: []
},
reducers: {
setData: (state, action: PayloadAction<any[]>) => {
state.data = action.payload
}
}
})
The above produces an error: TS2322: Type 'any[]' is not assignable to type 'never[]'
@denisw , @jessidhia , @Dudeonyx : any thoughts?
@Rinaldo yours is a separate issue. The type of data is never[] - you need to give it a type with either an interface or a cast:
const slice = createSlice({
initialState: {
data: [] as any[]
},
reducers: {
setData: (state, action: PayloadAction<any[]>) => {
state.data = action.payload
}
}
})
//
// or
//
interface State {
data: any[];
}
const initialState: State = {
data: [],
}
const state = createSlice({
initialState: initialState,
reducers: {
setData: (state, action: PayloadAction<any[]>) => {
state.data = action.payload
}
}
})
@pat-son I had tried the first option before posting and just now tried the second one. Neither of those snippets work for me, they both still produce the same error.
running redux-starter-kit 0.4.3 and typescript 3.4.2
I'd be happy to help but I'm out of town without a system until tomorrow
evening...
@Rinaldo have you tried createSlice with type parameters?
I don't recall the exact signature but it should be something like
const { reducer } = createSlice<Actions,State, Something>({
//...
});
@Dudeonyx That didn't seem to help, and I also noticed the same error with createReducer.
const initialState = {
data: [],
}
const reducer = createReducer(initialState, {
setData: (state, action: PayloadAction<any[]>) => {
state.data = action.payload // TS2322: Type 'any[]' is not assignable to type 'never[]'
}
})
It doesn't seem to matter whether the state is explicitly typed or not and it seems to only be a problem with arrays. Other types come through fine.
Should I open another issue or keep the discussion here?
Interestingly, the example works compiles fine in TypeScript 3.2.2 (the one we test against within redux-starter-kit), and the overall action type of the slice is inferred as PayloadAction<any>. However, in TypeScript 3.4 compilation fails as subscribed.
I suspect the change in behavior stems from a change in 3.4:
__Higher order type inference from generic functions__
TypeScript 3.4 can now produce generic function types when inference from other generic functions produces free type variables for inferences. This means many function composition patterns now work better in 3.4.
Sounds related, but I'm not 100% sure.
Anyway, a pragmatic solution would be to change the type signature of createSlice to expect PayloadAction<any> everywhere. Users of te function can still specify more precise action types for their case reducers as @pat-son has done, but there will be no weird type errors.
I think I found a way to restructure the typings so that the createSlice and createReducer type inference works better. I created a PR (#133), feel free to test whether it fixes your problem.
I've been fully busy with React-Redux stuff (and real work!) lately, but I will try to swing through and deal with several outstanding RSK PRs / issues within the next few days.
Most helpful comment
I've been fully busy with React-Redux stuff (and real work!) lately, but I will try to swing through and deal with several outstanding RSK PRs / issues within the next few days.