我正在尝试在 RTK 中创建一个减速器,它采用两个共享通用类型的参数。然而,似乎不可能在
reducer
和 prepare
函数之间共享通用类型。
这是我想要实现的一个例子
type SetPayload<T> = {
thingToBeSet: MyClass<T>;
value: T;
}
export const mySlice = createSlice({
name: 'mySlice',
initialState,
reducers: {
myReducer: {
reducer: (state, action: PayloadAction<SetPayload<any>>) => {
// Modify state here
},
prepare: <T>(thingToBeSet: MyClass<T>, value: T) => {
return {
payload: { thingToBeSet, value }
}
}
}
}
});
如上面的代码所示,我的
prepare
函数使用了一个泛型参数(因为thingToBeSet 上的某些属性的类型必须与值的类型相匹配)。但是,此类型参数不能用于 reducer 函数中的 action
类型 - 我尝试将其设置为 any
这给出了这个错误:
The types of 'payload.thingToBeSet' are incompatible between these types.
Type 'MyClass<never>' is not assignable to type 'MyClass<any>'.
Type 'any' is not assignable to type 'never'
因为
any
不能转换为 T
- 但是我怎么能在减速器和准备函数之间共享 T
呢?
我想我想要的是以某种方式为 myReducer 对象创建一个通用对象文字,像这样(不幸的是无效的 TS - 这可能吗?):
myReducer: <T> {
reducer: (state, action: PayloadAction<SetPayload<T>>) => {
// Modify state here
},
prepare: (thingToBeSet: MyClass<T>, value: T) => {
return {
payload: { thingToBeSet, value }
}
}
}
任何帮助将不胜感激。
不幸的是,这是不可能的。这些类型在内部映射以创建一个新的动作创建者函数——映射的类型不能继承通用功能。
你能做的最好的事情就是在导出之前将该动作生成器转换为你的通用函数类型。
export const myReducer = slice.actions.myReducer as <T>(thingToBeSet: MyClass<T>, value: T) => PayloadAction<SetPayload<T>>)
我为此使用的最终解决方案是在 createSlice 之外手动创建操作,并为减速器使用 extraReducers 属性。在函数上设置
type
属性允许自动输入和与 RTK 类似的用法。这更冗长而且不理想,但它至少在 reducer 中给出了正确的类型和隐式类型。
动作:
const myActionName = 'myAction';
export function myAction<T>(thingToBeSet: MyClass<T>, value: T): PayloadAction<SetPayload<T>> {
return {
type: myActionName,
payload: { thingToBeSet, value }
}
}
myAction.type = myActionName;
减速器:
createSlice({
...
extraReducers: (builder) => {
builder
.addCase(myAction, (state, action) => {
// Perform state changes
// `state` and `action` have correct implicit types
// (`action` is of type `PayloadAction<SetPayload<unknown>>`)
})
}
});
您可以使用包装函数
makeSlice<MyType>(...)
使泛型 T
可用于您的 prepare()
和您的 reducer()
函数。这种技术派上用场,例如使用 reduxToolkit 创建通用类型的 CRUD reducer 时。确保也将泛型类型传递给createSlice<...>
,否则你会被类型错误打脸。
export const makeSlice = <T>(name: string, initialState: T[] = []) =>
createSlice<T[], SliceCaseReducers<T[]>, string>({
name,
initialState,
reducers: {
myReducer: {
reducer: (state, action: PayloadAction<T>) => {
// modify state here
},
prepare: (value: T) => {
const id = nanoid();
return {
payload: { id, value },
};
},
},
},
});