为啥新的 `Pick<T, K extends keyof T>` 类型允许在 React 的 `setState()` 中使用 `K` 的子集?
Posted
技术标签:
【中文标题】为啥新的 `Pick<T, K extends keyof T>` 类型允许在 React 的 `setState()` 中使用 `K` 的子集?【英文标题】:Why does the new `Pick<T, K extends keyof T>` type allow subsets of `K` in React's `setState()`?为什么新的 `Pick<T, K extends keyof T>` 类型允许在 React 的 `setState()` 中使用 `K` 的子集? 【发布时间】:2017-07-25 18:56:31 【问题描述】:我以为我理解了新的TS 2.1 Pick
type 的用途,但后来我看到了how it was being used in the React type definitions,我不明白:
declare class Component<S>
setState<K extends keyof S>(state: Pick<S, K>, callback?: () => any): void;
state: Readonly<S>;
这允许您这样做:
interface PersonProps
name: string;
age: number;
class Person extends Component<, PersonProps>
test()
this.setState( age: 123 );
我的困惑是keyof S
是 name, age
,但我只用age
调用setState()
——为什么它不抱怨缺少name
?
我的第一个想法是,因为Pick
是一种索引类型,所以它根本不需要所有键都存在。说得通。但是如果我尝试直接分配类型:
const ageState: Pick<PersonProps, keyof PersonProps> = age: 123 ;
它确实抱怨缺少name
键:
Type ' age: number; ' is not assignable to type 'Pick<PersonProps, "name" | "age">'.
Property 'name' is missing in type ' age: number; '.
我不明白这一点。 似乎我所做的只是用 S
已分配给的类型填写 S
,然后它从允许 子集键变为需要所有键。这是一个很大的区别。 Here it is in the Playground。谁能解释这种行为?
【问题讨论】:
【参考方案1】:简答:如果你真的想要一个显式类型,你可以使用Pick<PersonProps, "age">
,但是使用隐式类型更容易。
长答案:
关键是K
是一个泛型类型变量,扩展 keyof T
。
类型keyof PersonProps
等于字符串联合"name" | "age"
。 "age"
类型可以说是对"name" | "age"
类型的扩展。
回忆Pick
的定义是:
type Pick<T, K extends keyof T> =
[P in K]: T[P];
这意味着对于每个K
,此类型描述的对象必须具有与T
中的属性K
相同类型的属性P
。您的示例游乐场代码是:
const person: Pick<PersonProps, keyof PersonProps> = age: 123 ;
解开泛型类型变量,我们得到:
Pick<T, K extends keyof T>
,
Pick<PersonProps, "name" | "age">
,
[P in "name" | "age"]: PersonProps[P]
,最后是
name: string, age: number
.
当然,这与 age: 123
不兼容。如果你改为说:
const person: Pick<PersonProps, "age"> = age: 123 ;
那么,按照同样的逻辑,person
的类型将适当地等同于age: number
。
当然,TypeScript 无论如何都会为你计算所有这些类型——这就是你得到错误的原因。由于 TypeScript 已经知道 age: number
和 Pick<PersonProps, "age">
类型是兼容的,所以你最好保留类型隐含:
const person = age: 123 ;
【讨论】:
"age"
类型可以说是对"name" | "age"
类型的扩展。 啊,我没想到,我会认为"name" | "age"
扩展了@ 987654349@,不是相反。谢谢你的解释!
很抱歉再次碰到这个问题,但是虽然我现在理解这种行为(谢谢!),但我无法真正合理化 "age"
是 "age" | "name"
的超类型而不是子类型类型,即使编译器清楚地看到它。这似乎与看似等效的接口 name, age
和 age
相互关联的方式相反。你能解释一下这个理由吗?
在泛型类型约束中,也许最好将“扩展”理解为“是更具体的类型”。考虑type AB = "a" | "b";
和type ABC = "a" | "b" | "c";
。如果您有一个变量const abc: ABC = "c";
,那么您不能将它分配给AB
类型的变量,但是AB
类型的任何有效值都是ABC
的有效值。由于AB
是ABC
的一种更具体的类型,所以AB
可以说是扩展 ABC
。
(哎呀,我最后的评论我使用了超类型/子类型。)谢谢你的解释,我明白了这个意思。我想extends
对我来说对联合类型有不熟悉的含义,但我认为现在它更有意义了。谢谢。
感谢您的回答。但是 React 怎么可能在参数中使用部分状态而不将 state 参数中的属性声明为可选的呢?以上是关于为啥新的 `Pick<T, K extends keyof T>` 类型允许在 React 的 `setState()` 中使用 `K` 的子集?的主要内容,如果未能解决你的问题,请参考以下文章
interface BaseDao<T extends Serializable> 为啥要这样写
如何理解 Java 中的 lt;T extends Comparable<