为什么新的`Pick <T,K扩展keyof T>`类型允许在React的`setState()中使用`K`的子集?

Aar*_*all 6 typescript reactjs typescript2.1

我以为我理解了新的TS 2.1 Pick类型的目的,但后来我看到它是如何在React类型定义中使用的,我不明白:

declare class Component<S> {
    setState<K extends keyof S>(state: Pick<S, K>, callback?: () => any): void;
    state: Readonly<S>;
}
Run Code Online (Sandbox Code Playgroud)

这允许你这样做:

interface PersonProps {
  name: string;
  age: number;
}

class Person extends Component<{}, PersonProps> {
  test() {
    this.setState({ age: 123 });
  }
}
Run Code Online (Sandbox Code Playgroud)

在这里,我的困惑是,keyof S{ name, age },但我打电话setState(),只有age-为什么没有抱怨缺少的name

我的第一个想法是因为Pick是索引类型,它根本不需要存在所有键.说得通.但是,如果我尝试直接分配类型:

const ageState: Pick<PersonProps, keyof PersonProps> = { age: 123 };
Run Code Online (Sandbox Code Playgroud)

确实抱怨丢失的name密钥:

Type '{ age: number; }' is not assignable to type 'Pick<PersonProps, "name" | "age">'.
  Property 'name' is missing in type '{ age: number; }'.
Run Code Online (Sandbox Code Playgroud)

我不明白这一点.这似乎是我所做的就是填补S与该类型S已经被分配给和它允许去子组键来要求所有的钥匙.这是一个很大的不同.这是在游乐场.谁能解释这种行为?

Ste*_*ett 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];
}
Run Code Online (Sandbox Code Playgroud)

这意味着对于每个K,通过这种类型的描述的对象必须有一个属性P相同的类型属性的KT.您的示例游乐场代码是:

const person: Pick<PersonProps, keyof PersonProps> = { age: 123 };
Run Code Online (Sandbox Code Playgroud)

展开泛型类型变量,我们得到:

  • 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 };
Run Code Online (Sandbox Code Playgroud)

然后,按照相同的逻辑,person将适当的类型相当于{age: number}.

当然,TypeScript无论如何都会为你计算所有这些类型 - 这就是你得到错误的方法.由于TypeScript已经知道类型{age: number}并且Pick<PersonProps, "age">兼容,因此您可以保持类型impicit:

const person = { age: 123 };
Run Code Online (Sandbox Code Playgroud)

  • 在泛型类型约束中,也许最好将"extends"读作"是更具体的类型".考虑`type AB ="a"| "b";`和`键入ABC ="a"| "b"| "C";`.如果你有一个变量`const abc:ABC ="c";`那么你就不能将它赋给`AB`类型的变量,但是`AB`类型的任何有效值都是`ABC`的有效值.由于"AB"是一种更具体的"ABC"类型,"AB"可以说是*扩展*`ABC`. (2认同)