使用动态/计算键选择<S, K> 类型

Posted

技术标签:

【中文标题】使用动态/计算键选择<S, K> 类型【英文标题】:Pick<S, K> type with dynamic/computed keys 【发布时间】: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 与动态键值一起使用?

【问题讨论】:

【参考方案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 );

【讨论】:

感谢您的精彩解释。对于这种情况,此问题是否已修复或任何其他解决方法?

以上是关于使用动态/计算键选择<S, K> 类型的主要内容,如果未能解决你的问题,请参考以下文章

如何从具有外键类型的jpa存储库返回选择查询

php中怎么打印数组啊?

java泛型&bean copy list

为啥新的 `Pick<T, K extends keyof T>` 类型允许在 React 的 `setState()` 中使用 `K` 的子集?

java Map<k,v>取值问题

计算float类型数据