Я не уверен, почему ты этого хочешь, но здесь идет ...
Используя идеи из этого ответа , чтобы преобразовать сопоставленный тип в пересечение функций, которые мы затем можем сопоставить с параметрами различных типов, мы можем определить 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]
Альтернативным подходом было бы использование UnionToIntersection
на keyof
интерфейса, но я считаю, что прохождение объединения может повлечь за собой больший риск потери порядка членов интерфейса. (Я полагаю, что в прошлом профсоюзы теряли порядок, хотя я не мог воспроизвести его в тестах на игровой площадке прямо сейчас.) В приведенном выше решении точно установлено, что перекрестки упорядочены, поэтому мы полагаемся только на сопоставленные типы для сохранения порядка и процесс вывода в IntersectionOfValues
для генерации контравариантных кандидатов по порядку и их пересечения по порядку. Это все еще поведение, зависящее от реализации, но я думаю, что оно вряд ли изменится.