Select type <S, K> with dynamic / computed keys

The latter @types/react ( v15.0.6 ) uses functions added in TypeScript 2.1 for setState , namely Pick<S, K> . This is good, because now the correct examples are correct, because before the update icons "did not know" that setState combines this.state rather than replacing it.

In addition, using Pick makes setState very strict in terms of valid input. It is no longer possible to add properties to state that are not defined in the component definition (second React.Component family React.Component .

But it is also more difficult to define a dynamic update handler. For instance:

 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 throws the following error:

 [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; }'. 

although the key type is "name" | "age" "name" | "age" .

I can not find a solution for this, except for the existence of a separate function updateName and updateAge . Does anyone know how to use Pick with dynamic key values?

+6
source share
1 answer

So, after doing more research, I can provide a little more information about 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. However, if you replace const with let ( let name = 'Bob' ), the name variable will be of type string .

This concept is called "extension". This basically means that the type system is trying to be as explicit as possible. Since const cannot be reassigned, TypeScript can infer the exact type. let statements can be reassigned. Thus, TypeScript will output string (in the example above) as the type name .

The same thing happens with const key = e.currentTarget.name as keyof Person . key will be of type (union) "name"|"age" , which is what we want. But in the expression this.setState({ [key]: value }); the key variable (incorrectly) expanded to string .


tl; dr; There seems to be a bug in TypeScript. I submitted this issue to the Github repository, and the TypeScript team investigates the issue . :)

As a temporary workaround, you can:

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

Source: https://habr.com/ru/post/1014763/


All Articles