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