简体   繁体   English

挑选<s, k>使用动态/计算键键入</s,>

[英]Pick<S, K> type with dynamic/computed keys

The latest @types/react ( v15.0.6 ) make use of features added in TypeScript 2.1 for setState , namely Pick<S, K> .最新的@types/react ( v15.0.6 ) 使用了 TypeScript 2.1 中为setState添加的功能,即Pick<S, K> Which is a good thing, because now the typings are correct, because before the update typings "didn't know" that setState is merging this.state , rather than replacing it.这是一件好事,因为现在输入是正确的,因为在更新输入之前“不知道” setState正在合并this.state ,而不是替换它。

Also, using Pick makes the setState function very strict in terms of allowed input.此外,使用Pick使setState函数在允许的输入方面非常严格。 It is no longer possible to add properties to the state that aren't defined in the component definition (second generic of React.Component .不再可能向组件定义中未定义的state添加属性( React.Component的第二个泛型。

But it is also harder to define a dynamic update handler.但定义动态更新处理程序也更难。 For example:例如:

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>
    );
  }
}

The setState function will throw the following error 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; }'.

even though the type of key is "name" | "age"即使key的类型是"name" | "age" "name" | "age" . "name" | "age"

I can not find a solution for this, other than having a separate updateName and updateAge function.除了具有单独的updateNameupdateAge函数之外,我找不到解决方案。 Does anyone know how to use Pick with dynamic key values?有谁知道如何将Pick与动态键值一起使用?

So after doing more research I can provide a little more context on what is happening in the above code.因此,在进行更多研究之后,我可以提供更多有关上述代码中发生的情况的上下文。

When you do something like const name = 'Bob' the type of the variable name is 'Bob' not string.当您执行类似const name = 'Bob'的操作时,变量name的类型是'Bob'而不是字符串。 However, if you replace the const with a let ( let name = 'Bob' ) the variable name will be of type string .但是,如果您将const替换为let ( let name = 'Bob' ),则变量name将为string类型。

This concept is called "widening".这个概念叫做“拓宽”。 Basically, it means that the type system tries to be as explicit as possible.基本上,这意味着类型系统试图尽可能明确。 Because const can not be reassigned TypeScript can infer the exact type.因为const不能被重新赋值,TypeScript 可以推断出确切的类型。 let statements can be reassigned. let语句可以重新赋值。 Thus, TypeScript will infer string (in the above example) as the type of name .因此,TypeScript 会将string (在上面的示例中)推断为name的类型。

The same is happening with const key = e.currentTarget.name as keyof Person . const key = e.currentTarget.name as keyof Person也会发生同样的情况。 key will be of (union) type "name"|"age" , which is exactly what we want it to be. key将是(联合)类型"name"|"age" ,这正是我们想要的。 But in the expression this.setState({ [key]: value });但是在表达式中this.setState({ [key]: value }); variable key is (incorrectly) widened to a string .变量key被(错误地)扩展为string


tl;dr; TL;博士; It seems like there is a bug in TypeScript. TypeScript 中似乎存在错误。 I posted the issue to the Github repo and the TypeScript team is investigating the problem .我将问题发布到 Github 存储库,TypeScript 团队正在调查该问题 :) :)

As a temporary workaround you can do:作为临时解决方法,您可以执行以下操作:

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

The answer from Sebastian no longer works for me (though at one stage it did).塞巴斯蒂安的回答不再对我有用(尽管在某个阶段确实如此)。 I now do the following until it is resolved in Typescript core (as per the issue listed in Sebastians answer):我现在执行以下操作,直到它在 Typescript 核心中得到解决(根据 Sebastians 回答中列出的问题):

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

It's lame, but it works这很蹩脚,但它有效

I think this might solve your problem.我认为这可能会解决您的问题。

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>)
}

This will still give an error appropriately if the value is not in the set of possible property value types, but will not give you an error if the keys are not within the set of possible property keys.如果该值不在可能的属性值类型集中,这仍然会适当地给出错误,但如果键不在可能的属性键集中,则不会给您错误。

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

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM