【问题标题】:Pick<S, K> type with dynamic/computed keys使用动态/计算键选择<S, K> 类型
【发布时间】:2017-06-24 17:13:02
【问题描述】:

最新的@types/react (v15.0.6) 利用TypeScript 2.1 中为setState 添加的功能,即Pick&lt;S, K&gt;。这是一件好事,因为现在打字是正确的,因为在更新之前打字“不知道”setState 正在合并 this.state,而不是替换它。

此外,使用Pick 使setState 函数在允许输入方面非常严格。无法再向 state 添加未在组件定义中定义的属性(React.Component 的第二个泛型。

但定义动态更新处理程序也比较困难。例如:

import * as React from 'react';


interface Person {
  name: string;
  age: number|undefined;
}


export default class PersonComponent extends React.Component<void, Person> {
  constructor(props:any) {
    super(props);

    this.state = { 
      name: '',
      age: undefined
    };
    this.handleUpdate = this.handleUpdate.bind(this);
  }

  handleUpdate (e:React.SyntheticEvent<HTMLInputElement>) {
    const key = e.currentTarget.name as keyof Person;
    const value = e.currentTarget.value;
    this.setState({ [key]: value });
  }

  render() {
    return (
      <form>
        <input type="text" name="name" value={this.state.name} onChange={this.handleUpdate} />
        <input type="text" name="age" value={this.state.age} onChange={this.handleUpdate} />
      </form>
    );
  }
}

setState 函数会抛出以下错误

[ts] Argument of type '{ [x: string]: string; }' is not assignable 
     to parameter of type 'Pick<Person, "name" | "age">'.
       Property 'name' is missing in type '{ [x: string]: string; }'.

即使key 的类型是"name" | "age"

除了有一个单独的updateNameupdateAge 函数之外,我找不到解决方案。有谁知道如何将Pick 与动态键值一起使用?

【问题讨论】:

    标签: reactjs generics typescript components


    【解决方案1】:

    我认为这可能会解决您的问题。

    type State = {
      name: string,
      age: number | undefined
    };
    
    type StateKeys = keyof State;
    
    dynSetState(key: StateKeys, value: string) {
      this.setState({
        [key]: value
      } as Pick<State, keyof State>)
    }
    

    如果值不在可能的属性值类型集合中,这仍然会适当地给出错误,但如果键不在可能的属性键集合中,则不会给出错误。

    参考:https://github.com/DefinitelyTyped/DefinitelyTyped/issues/26635

    【讨论】:

      【解决方案2】:

      塞巴斯蒂安的答案不再对我有用(尽管在某个阶段它确实有效)。我现在执行以下操作,直到它在 Typescript 核心中得到解决(根据 Sebastians 回答中列出的问题):

        handleUpdate (e:React.SyntheticEvent<HTMLInputElement>) {
          const newState = {};
          newState[e.currentTarget.name] = e.currentTarget.value;
          this.setState(newState);
        }
      

      虽然很烂,但是很管用

      【讨论】:

        【解决方案3】:

        因此,在进行更多研究后,我可以提供更多关于上述代码中发生的情况的背景信息。

        当您执行const name = 'Bob' 之类的操作时,变量name 的类型是'Bob' 不是 字符串。但是,如果将const 替换为let (let name = 'Bob'),则变量name 的类型将是string

        这个概念被称为“加宽”。基本上,这意味着类型系统试图尽可能明确。因为const 不能重新赋值 TypeScript 可以推断出确切的类型。 let 语句可以重新分配。因此,TypeScript 将推断string(在上面的示例中)作为name 的类型。

        const key = e.currentTarget.name as keyof Person 也是如此。 key 将是(联合)类型"name"|"age",这正是我们想要的。 但是在表达式this.setState({ [key]: value }); 变量key 被(不正确地)扩大到string


        tl;dr; 似乎 TypeScript 中存在错误。我将问题发布到 Github 存储库和 TypeScript 团队 is investigating the problem。 :)

        作为临时解决方法,您可以这样做:

        this.setState({ [key as any]: value });
        

        【讨论】:

        • 感谢您的精彩解释。对于这种情况,此问题是否已修复或任何其他解决方法?
        猜你喜欢
        • 1970-01-01
        • 2013-02-24
        • 1970-01-01
        • 2013-04-03
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2020-10-29
        • 1970-01-01
        相关资源
        最近更新 更多