Why does the new type `Pick <T, K extends keyof T>` allow subsets of `K` in React` setState ()`?

I thought I understood the purpose of the new TS 2.1 Pick type , but then I saw how it was used in React type definitions , and I don't understand:

 declare class Component<S> { setState<K extends keyof S>(state: Pick<S, K>, callback?: () => any): void; state: Readonly<S>; } 

What allows you to do this:

 interface PersonProps { name: string; age: number; } class Person extends Component<{}, PersonProps> { test() { this.setState({ age: 123 }); } } 

My confusion here is that keyof S is { name, age } , but I only call setState() with age - why doesn't it complain about the missing name ?

My first thought is that since Pick is an index type, it just does not require all the keys. Has the meaning. But if I try to assign the type directly:

 const ageState: Pick<PersonProps, keyof PersonProps> = { age: 123 }; 

He complains about the missing name key:

 Type '{ age: number; }' is not assignable to type 'Pick<PersonProps, "name" | "age">'. Property 'name' is missing in type '{ age: number; }'. 

I do not understand this. It seems that all I did was populate S type to which S has already been assigned, and it has moved from providing a subset of keys that require all keys. This is a big difference. Here he is on the playground . Can anyone explain this behavior?

+5
source share
1 answer

Short answer: if you really need an explicit type, you can use Pick<PersonProps, "age"> , but instead, it is easier to use implicit types.

Long answer:

The key point is that K is a generic variable that extends keyof T

Type keyof PersonProps equals string concatenation "name" | "age" "name" | "age" . Type "age" can be said to extend type "name" | "age" "name" | "age" .

Recall that the definition of Pick :

 type Pick<T, K extends keyof T> = { [P in K]: T[P]; } 

which means that for every K object described by this type must have property P the same type as property K in T Your sample playground code:

 const person: Pick<PersonProps, keyof PersonProps> = { age: 123 }; 

Deploying general type variables, we get:

  • Pick<T, K extends keyof T> ,
  • Pick<PersonProps, "name" | "age"> Pick<PersonProps, "name" | "age"> ,
  • [P in "name" | "age"]: PersonProps[P] [P in "name" | "age"]: PersonProps[P] and finally
  • {name: string, age: number} .

This, of course, is incompatible with { age: 123 } . If you say:

 const person: Pick<PersonProps, "age"> = { age: 123 }; 

then, following the same logic, the type person will be correctly equivalent to {age: number} .

Of course, TypeScript computes all of these types for you anyway - how you got the error. Since TypeScript already knows that the types {age: number} and Pick<PersonProps, "age"> compatible, you can also keep the type implicit:

 const person = { age: 123 }; 
+5
source

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


All Articles