【问题标题】: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。谁能解释这种行为?

【问题讨论】:

    标签: reactjs typescript typescript2.1


    【解决方案1】:

    简答:如果你真的想要一个显式类型,你可以使用Pick&lt;PersonProps, "age"&gt;,但是使用隐式类型更容易。

    长答案:

    关键是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&lt;T, K extends keyof T&gt;,
    • Pick&lt;PersonProps, "name" | "age"&gt;,
    • [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&lt;PersonProps, "age"&gt; 类型是兼容的,所以你最好保留类型隐含:

    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 的有效值。由于ABABC 的一种更具体的类型,所以AB 可以说是扩展 ABC
    • (哎呀,我最后的评论我使用了超类型/子类型。)谢谢你的解释,我明白了这个意思。我想extends 对我来说对联合类型有不熟悉的含义,但我认为现在它更有意义了。谢谢。
    • 感谢您的回答。但是 React 怎么可能在参数中使用部分状态而不将 state 参数中的属性声明为可选的呢?
    猜你喜欢
    • 2022-01-21
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2018-05-30
    • 1970-01-01
    相关资源
    最近更新 更多