为什么新的`Pick <t,k =“”extends =“”keyof =“”t =“”>`类型允许在React的`setState()`中设置`K`的子集?

时间:2017-03-06 02:27:26

标签: reactjs typescript typescript2.1

我以为我理解了新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 },但我只用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; }'. 密钥:

S

我不明白这一点。它似乎我所做的就是用S已经分配给{{1}}的类型填写{{1}},它来自允许子集需要所有键的键。这是一个很大的不同。 Here it is in the Playground。任何人都可以解释这种行为吗?

1 个答案:

答案 0 :(得分:8)

简答:如果您真的想要一个显式类型,可以使用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,此类型描述的对象必须具有与P中的属性K相同类型的属性T。您的示例游乐场代码是:

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">是兼容的,因此您可以将类型保留为impicit:

const person = { age: 123 };