Я взял функцию-генератор zip и пытаюсь определить тип результата. Учитывая zip(A, B) с A Iterable<string> и B Iterable<number>, я бы хотел определить тип возвращаемого значения Generator<[string, number][]>
Вот что я получил на данный момент:
declare function zip<T extends Iterable<any>[]>(...iterables: T): Generator<T>:
Проблема в том, что вместо этого он выводит объединение типов Generator<[string[], number[]]>.
Самое близкое, что мне кажется, это что-то вроде этого:
declare function zip<
T extends Iterable<any>[],
K = T extends Iterable<infer K>[] ? K : never,
>(...iterables: T): Generator<[...K[]]>
но это все равно дает Generator<(string | number)[]>, пытаясь сделать вывод на месте, я получаю Generator<any[]>:
declare function zip<T extends Iterable<any>[]>(
...iterables: T
): Generator<[...(T extends Iterable<infer K>[] ? K : never)]>
Мой желаемый результат:
declare const A: string[];
declare const B: Iterable<number>;
const z = zip(A, B);
// ^? const z: Generator<[string, number][]>
Да, не стесняйтесь игнорировать тело функции. Я удалил их, чтобы улучшить четкость. Я знаю, что вы даете ответы высшего уровня, и мне бы очень хотелось узнать здесь, как я могу предотвратить потерю этой позиционной информации аргументов остальных параметров. Это проблема, с которой я часто сталкиваюсь.
Итак, эта ссылка на игровую площадку то, что вы ищете? (Было бы неплохо иметь минимальный воспроизводимый пример , где тестовые примеры уже были записаны для нас.) Если да, я напишу ответ с объяснением; если нет, пожалуйста, отредактируйте, чтобы показать неудовлетворительные варианты использования.
идеальный! Наверное, меня сбила с правильного подхода ошибка TS rest parameters must be an array type? Но ваш ...iterables: { [I in keyof T]: Iterable<T[I]> } не является типом массива? Должен сказать, что я тоже не знаю, что там происходит с взаимодействиями I и T. Очень признателен, если вы сможете написать ответ, объясняющий шаги.
Гарантирован ли порядок кортежа? как это определяется?
Да, я хочу, чтобы этот тип был заказан. Если на входе f(string[], number[]), на выходе должно быть Generator<[string, number][]>, но если это, например, f(number[], string[], any[]), то должно быть именно Generator<[number, string, any][]>
Это сопоставленный тип с массивом, который дает другой массив (я включил ссылку на документацию в свой ответ ниже)






Я был бы склонен использовать следующую позывную:
declare function zip<T extends any[]>(
...iterables: { [I in keyof T]: Iterable<T[I]> }
): Generator<T[]>;
Здесь параметр общего типа T соответствует типу элемента ожидаемого результата, поэтому в вашем примере zip(A, B), где A — это Iterable<X>, а B — это Iterable<Y>, то T будет [X, Y].
Тогда параметр iterables rest имеет тип отображаемого массива/кортежа { [I in keyof T]: Iterable<T[I]> }. Сопоставленный массив/кортеж превращает массивы/кортежи в массивы/кортежи и, по сути, выполняет итерацию только по числовым/числовым индексам массива/кортежа. Это означает, что вы можете думать о I в keyof T как об итерации "0", "1" и т. д. вплоть до наибольшего индекса кортежа. Таким образом, {[I in keyof T]: Iterable<T[I]>} превратит кортеж типов в кортеж итераций этих типов (например, [X, Y] станет [Iterable<X>, Iterable<Y>]). Поскольку это гомоморфный отображаемый тип (см. Что означает «гомоморфный отображаемый тип»?), TypeScript может выводить T из { [I in keyof T]: Iterable<T[I]> }.
Давайте проверим это:
declare const A: string[];
declare const B: Iterable<number>;
const z = zip(A, B);
// ^? const z: Generator<[string, number][]>
Выглядит неплохо. Учитывая оставшийся входной аргумент типа [string[], Iterable<number>], компилятор может вывести T как [string, number], а затем выходные данные будут иметь тип Generator<[string, number][]> по желанию.
Можем ли мы полностью игнорировать реализацию? Вы просто хотите печатать, верно?