【问题标题】:keyof and React Redux actionkeyof 和 React Redux 操作
【发布时间】:2021-10-05 03:49:16
【问题描述】:

我正在使用 React Redux 工具包和 keyof 来指定我的操作负载的一个元素应该是我的状态所包含的类型的键,因此我可以使用 redux 操作更新状态的属性。 无论如何,它说: 输入字符串 | number 不能分配给从不输入。 在这一行:

state[id][key] = value;

你能给我解释一下这里的问题吗? 非常感谢!

interface MyType {
  a: number;
  b: string;
  c: number;
};

const makeMyType = () => {
  return {
    a: 1,
    b: 'b',
    c: 2
  } as MyType;
}

interface UpdateType<Type> {
  id: number;
  key: keyof Type;
  value: Type[keyof Type];
}

const test_slice = createSlice({
  name: 'test_slice',
  initialState: [makeMyType(), makeMyType()];
  reducers: {
    updateProperty(state: MyType[], action: PayloadAction<UpdateType<MyType>) {
      const {id, key, value} = action.payload;
      state[id][key] = value;
    }
  }
});

【问题讨论】:

    标签: reactjs typescript redux


    【解决方案1】:

    这是打字稿突变的经典问题。 你可以找到这个in my blog和其他SO答案的完整和详细的解释:[firstsecondthird]

    TL;博士

    同样,同一类型变量在逆变位置的多个候选会导致推断出交集类型。

    对象的键类型是逆变的

    因此,state[id][key] 会产生此错误:

    Type 'string | number' is not assignable to type 'never'.
      Type 'string' is not assignable to type 'never'
    

    这是因为 string &amp; 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

    【讨论】:

      猜你喜欢
      • 2020-02-25
      • 1970-01-01
      • 1970-01-01
      • 2017-11-05
      • 2017-11-09
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2017-10-09
      相关资源
      最近更新 更多