[英]Why does the new `Pick<T, K extends keyof T>` type allow subsets of `K` in React's `setState()`?
I thought I understood the purpose of the new TS 2.1 Pick
type , but then I saw how it was being used in the React type definitions and I don't understand: 我以为我理解了新的TS 2.1
Pick
类型的目的,但后来我看到它是如何在React类型定义中使用的 ,我不明白:
declare class Component<S> {
setState<K extends keyof S>(state: Pick<S, K>, callback?: () => any): void;
state: Readonly<S>;
}
Which 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 call setState()
with only age
-- why doesn't it complain about the missing name
? 我的困惑在于,
keyof S
是{ name, age }
但是我只用age
调用setState()
- 为什么不抱怨丢失的name
?
My first thought is that because Pick
is an index type, it simply doesn't require all the keys to exist. 我的第一个想法是因为
Pick
是一种索引类型,它根本不需要存在所有键。 Makes sense. 说得通。 But if I try to assign the type directly:
但是,如果我尝试直接分配类型:
const ageState: Pick<PersonProps, keyof PersonProps> = { age: 123 };
It does complain about the missing name
key: 它确实抱怨缺少
name
密钥:
Type '{ age: number; }' is not assignable to type 'Pick<PersonProps, "name" | "age">'.
Property 'name' is missing in type '{ age: number; }'.
I don't understand this. 我不明白这一点。 It seems all I did was fill in
S
with the type that S
is already assigned to, and it went from allowing a sub-set of keys to requiring all keys. 似乎我所做的就是用
S
已经分配的类型填充S
,并且它从允许一组键到需要所有键。 This is a big difference. 这是一个很大的不同。 Here it is in the Playground .
这是在游乐场 。 Can anyone explain this behavior?
谁能解释这种行为?
Short answer: if you really want an explicit type, you can use Pick<PersonProps, "age">
, but it's easier use implicit types instead. 简短回答:如果你真的想要一个显式类型,你可以使用
Pick<PersonProps, "age">
,但更容易使用隐式类型。
Long answer: 答案很长:
The key point is that the K
is a generic type variable which extends keyof T
. 关键点在于
K
是一个泛型类型变量,它扩展 keyof T
。
The type keyof PersonProps
is equal to the string union "name" | "age"
keyof PersonProps
的类型keyof PersonProps
等于字符串union "name" | "age"
"name" | "age"
. "name" | "age"
。 The type "age"
can be said to extend the type "name" | "age"
类型
"age"
可以说是扩展类型"name" | "age"
"name" | "age"
. "name" | "age"
。
Recall the definition of Pick
is: 回想一下
Pick
的定义是:
type Pick<T, K extends keyof T> = {
[P in K]: T[P];
}
which means for every K
, the object described by this type must have a property P
of same type as the property K
in T
. 这意味着对于每个
K
,此类型描述的对象必须具有与T
的属性K
相同类型的属性P
Your example playground code was: 你的示例游乐场代码是:
const person: Pick<PersonProps, keyof PersonProps> = { age: 123 };
Unwrapping the generic type variables, we get: 展开泛型类型变量,我们得到:
Pick<T, K extends keyof T>
, Pick<T, K extends keyof T>
, Pick<PersonProps, "name" | "age">
Pick<PersonProps, "name" | "age">
, Pick<PersonProps, "name" | "age">
, [P in "name" | "age"]: PersonProps[P]
[P in "name" | "age"]: PersonProps[P]
, and finally [P in "name" | "age"]: PersonProps[P]
,最后 {name: string, age: number}
. {name: string, age: number}
。 This is, of course, incompatible with { age: 123 }
. 当然,这与
{ age: 123 }
不相容。 If you instead say: 如果你改为说:
const person: Pick<PersonProps, "age"> = { age: 123 };
then, following the same logic, the type of person
will properly be equivalent to {age: number}
. 然后,按照相同的逻辑,类型
person
将适当地等同于{age: number}
。
Of course, TypeScript is calculating all of these types for you anyway—that's how you got the error. 当然,TypeScript无论如何都会为你计算所有这些类型 - 这就是你得到错误的方法。 Since TypeScript already knows the types
{age: number}
and Pick<PersonProps, "age">
are compatible, you might as well keep the type impicit: 由于TypeScript已经知道类型
{age: number}
和Pick<PersonProps, "age">
是兼容的,因此您可以保持类型impicit:
const person = { age: 123 };
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.