typescript - 两个 Pick 工具类型实现之间有什么区别
我正在尝试这个:https://github.com/type-challenges/type-challenges/blob/master/questions/4-easy-pick/README.md
我无法理解它们之间的区别:
type MyPick<T, K> = T extends {} ? K extends keyof T ? {[P in K]: T[P]} : never : never
type MyPick<T extends {}, K extends keyof T> = {[P in K]: T[P]};
你可以在这里玩它:输入链接描述
测试用例:
type A = {name: string; value: number;}
type B = MyPick1<A, 'name' | 'value'> // got {name: string} | {value: number}
type C = MyPick2<A, 'name' | 'value'> // got {name: string; value: number;}
我对结果感到困惑,这是为什么?
答案#1
首先 MyPick:
type MyPick<T, K> = T extends {} ? K extends keyof T ? {[P in K]: T[P]} : never : never
可以接受任意两个通用类型。我的意思是,你可以使用 MyPick<1,2>/never 或 MyPick<[], false>/never 调用它,当然这没有意义,但仍然被允许,因为约束是在实用工具的主体内部检查的。
第二个 MyPick:
type MyPick<T extends {}, K extends keyof T> = {[P in K]: T[P]};
这个 MyPick 对两个参数都有约束。第一个参数应该扩展 {},实际上是 any 类型,第二个参数应该是第一个参数的键。
例如:
type Test0 = MyPick1<2, 'toString'> // ok
type Test1 = MyPick1<2, 3> // expected error
正如你可能已经注意到的,你仍然可以将第一个参数作为原始类型传递,因为在 JS 中每个值都是对象,因此都有自己的键。
// "toString" | "toFixed" | "toExponential" | "toPrecision" | "valueOf" | "toLocaleString"
type NumberKeys = keyof number;
我假设你想应用一个约束,即用户只允许传递对象作为第一个参数,这意味着你应该使用 Record<PropertyKey, any> 约束。例如:
type Test0 = MyPick1<{ age: number }, 'age'> // ok
type Test1 = MyPick1<2, 'toString'> // error
type Test2 = MyPick1<true, 3> // expected error
为什么 MyPick1 返回一个联合类型?
看这个:
// Slightly reduced
type MyPick1<T, K> = K extends keyof T ? { [P in K]: T[P] } : never
type A = { name: string; value: number; }
type B = MyPick1<A, 'name' | 'value'> // got {name: string} | {value: number}
MyPick1 返回联合类型是由于分配性。请仔细阅读文档的这一部分。这是非常重要的功能。
K 联合类型会分发,这意味着这一行 { [P in K]: T[P] } 会应用到每个联合成员。
再看一个例子,以使其更清楚:
type Distibutive<T> = T extends any ? T[] : never
type Result1 = Distibutive<'a' | 'b'> // "a"[] | "b"[]
// This is how you disable distributivity
type NonDistibutive<T> = [T] extends [any] ? T[] : never
type Result2 = NonDistibutive<'a' | 'b'> // ("a" | "b")[]
