我不确定你为什么想要这个,但是这里是......
使用this answer 中的想法将映射类型转换为函数的交集,然后我们可以匹配不同的类型参数,我们可以定义ToTuple 以适用于固定的最大成员数原始界面。
type IntersectionOfValues<T> =
{[K in keyof T]: (p: T[K]) => void} extends
{[n: string]: (p: infer I) => void} ? I : never;
type IntersectionOfFunctionsToType<F, T> =
F extends {
(na: infer NA, a: infer A): void;
(nb: infer NB, b: infer B): void;
(nc: infer NC, c: infer C): void;
} ? [NA, A, NB, B, NC, C] :
F extends {
(na: infer NA, a: infer A): void;
(nb: infer NB, b: infer B): void;
} ? [NA, A, NB, B] :
F extends {
(na: infer NA, a: infer A): void
} ? [NA, A] :
never;
type ToTuple<T> = IntersectionOfFunctionsToType<
IntersectionOfValues<{ [K in keyof T]: (k: K, v: T[K]) => void }>, T>;
interface Person {
name: string,
age: number
}
type Args = ToTuple<Person> // ['name', string, 'age', number]
另一种方法是在keyof 接口上使用UnionToIntersection,但我认为通过联合可能会带来更大的丢失接口成员顺序的风险。 (我相信我过去看到过工会失去秩序,虽然我刚才在操场上的测试中无法重现它。)在上面的解决方案中,已经确定交叉点是有序的,所以我们只依赖映射类型以保持顺序和IntersectionOfValues 中的推理过程以按顺序生成逆变候选并按顺序与它们相交。这仍然是依赖于实现的行为,但我认为它不太可能改变。