2017-03-06 49 views
5

我想我理解新TS 2.1 Pick type的目的,但后来我看到how it was being used in the React type definitions,我不明白:为什么新的`Pick <T,K extends T>`类型允许React的`setState()`中的`K`的子集?

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 }但我叫setState()只有age - 为什么它不抱怨失踪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; }'. 

我不明白这一点。它似乎我所做的就是填补SS已分配的类型,它从允许走了子组键到需要所有键。这是一个很大的区别。 Here it is in the Playground。谁能解释这种行为?

回答

5

简短回答:如果你真的想要一个明确的类型,你可以使用Pick<PersonProps, "age">,但它更容易使用隐式类型。

龙答:

关键的一点是,K延伸keyof T泛型类型变量。

类型keyof PersonProps等于字符串union "name" | "age"。类型"age"可以说是扩展类型"name" | "age"

召回Pick的定义是:

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

,这意味着对于每个K,通过这种类型的描述的对象必须具有相同类型的属性PT属性K。你的榜样操场代码为:

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正在为你计算所有这些类型 - 这就是你如何得到错误。由于打字稿已经知道的类型{age: number}Pick<PersonProps, "age">是兼容的,你还不如保持类型impicit:

const person = { age: 123 }; 
+0

*类型'“年龄”'可以说是延伸型'“名” | “age”'。* 啊,我不会猜到,我会认为''name'| “年龄”延长了“年龄”,而不是其他方式。感谢您的解释! – Aaron

+0

不好意思再说一遍,但是现在我明白了这个行为(谢谢!),但我不能真正理解“年龄”是如何超越“年龄”的。 “name”而不是子类型,即使清楚地知道编译器如何看到它。似乎最上面的看起来与表面上相同的接口“{name,age}”和“{age}”相互关联是相反的。你能解释这个基本原理吗? – Aaron

+1

在泛型类型约束中,可能最好将“extends”理解为“是更具体的类型”。考虑'输入AB =“a”| “b”;'和'输入ABC =“a”| “b”| “C”;'。如果你有一个变量'const abc:ABC =“c”;'那么你不能把它分配给一个类型为'AB'的变量,但任何有效的AB类型值都是'ABC'的有效值。由于'AB'是更具体的'ABC'类型,'AB'可以说是* extend *'ABC'。 –