这是打字稿突变的经典问题。
你可以找到这个in my blog和其他SO答案的完整和详细的解释:[first,second,third]
TL;博士
同样,同一类型变量在逆变位置的多个候选会导致推断出交集类型。
与
对象的键类型是逆变的
因此,state[id][key] 会产生此错误:
Type 'string | number' is not assignable to type 'never'.
Type 'string' is not assignable to type 'never'
这是因为 string & number = never。见第一个报价:.... contra-variant positions causes an intersection。
TypeScript 不确定 state[id][key] = value。
这种类型:
interface UpdateType<Type> {
id: number;
key: keyof Type;
value: Type[keyof Type];
}
是弱的,允许表示非法状态。
考虑下一个例子:
const x: UpdateType<MyType> = {
id: 2,
key: 'a',
value: 's' //<--- should be number
}
如果你想让它更安全,你应该使用所有允许/合法状态的联合:
type Values<T> = T[keyof T]
/**
* Is a union of all valid states
*/
type UpdateType<Type> = Values<{
[Key in keyof Type]: {
id: number;
key: Key;
value: Type[Key];
}
}>
但这并没有帮助我们解决问题。
如果你想修复它,你应该上一级并且只改变state[id]。该值具有MyType 类型。
我们应该注意一件重要的事情 - TS 不会跟踪突变。
我们如何从中受益?
考虑这个例子:
const mutableUpdate = <
State extends MyType,
Key extends keyof State,
Value extends State[Key]
>(state: State, key: Key, value: Value) => {
state[key] = value;
return state
}
上面的函数将帮助我们改变状态。
完整示例:
import { createSlice, PayloadAction } from '@reduxjs/toolkit'
interface MyType {
a: number;
b: string;
c: number;
};
const makeMyType = (): MyType => ({
a: 1,
b: 'b',
c: 2
})
type Values<T> = T[keyof T]
/**
* Is a union of all valid states
*/
type UpdateType<Type> = Values<{
[Key in keyof Type]: {
id: number;
key: Key;
value: Type[Key];
}
}>
const mutableUpdate = <
State extends MyType,
Key extends keyof State,
Value extends State[Key]
>(state: State, key: Key, value: Value) => {
state[key] = value;
return state
}
const test_slice = createSlice({
name: 'test_slice',
initialState: [makeMyType(), makeMyType()],
reducers: {
updateProperty(state: MyType[], action: PayloadAction<UpdateType<MyType>>) {
const { id, key, value } = action.payload;
const result = mutableUpdate(state[id], key, value);
state[id] = result;
}
}
});
Playground