我以为我理解了新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。任何人都可以解释这种行为吗?
答案 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 };