这里的问题是T[K] extends string ? [K] : [never]
在K
个成员中不超过union个.它不等于(T["x"] extends string ? ["x"] : [never]) | (T["y"] extends string ? ["y"] : [never])
.
只有在distributive conditional type的情况下,才会出现这种自动拆分和合并联合的情况,其中选中的类型是要在其联合成员上分发的类型参数.但T[K]
不是一个类型参数(T
是一个类型参数,K
是一个类型参数,但T[K]
不是…很像t
可能是一个变量,k
可能是一个变量,但t[k]
可能不是),所以T[K] extends ... ? ... : ...
根本不会分布在联合体上.在任何情况下,你都希望在K
而不是T[K]
年的unions 中进行分配.
所以你的PickKeys
等于
type PickKeys1<T extends object> =
T[keyof T] extends string ? [keyof T] : [never];
如果你插上TestType
,你会得到
type PickKeysTestType =
TestType[keyof TestType] extends string ? [keyof TestType] : [never];
type PickKeysTestType1 =
(string | number) extends string ? ["x" | "y"] : [never];
type PickKeysTestType2 =
[never];
因为string | number
不是string
的子类型,所以条件类型的计算结果是false分支,它只是never
.哎呀.
如果你想在K
个联合体中进行分配,你可以用一个"无操作"的分配条件类型来包装整个过程:
type PickKeys2<T extends object, K extends keyof T = keyof T> =
K extends unknown ? T[K] extends string ? [K] : [never] : never;
然后事情按预期进行:
function testFn<T extends object>(...keys: PickKeys2<T>) { }
testFn<TestType>("x"); // okay
有other ways个实现类似PickKeys
的东西(我不确定为什么我们需要元组),但这超出了问题的范围.
Playground link to code