Как определить тип элемента массива для каждого оставшегося параметра и создать из него тип кортежа?

Я взял функцию-генератор 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][]>

Можем ли мы полностью игнорировать реализацию? Вы просто хотите печатать, верно?

jcalz 30.03.2024 17:22

Да, не стесняйтесь игнорировать тело функции. Я удалил их, чтобы улучшить четкость. Я знаю, что вы даете ответы высшего уровня, и мне бы очень хотелось узнать здесь, как я могу предотвратить потерю этой позиционной информации аргументов остальных параметров. Это проблема, с которой я часто сталкиваюсь.

Robin De Schepper 30.03.2024 17:23

идеальный! Наверное, меня сбила с правильного подхода ошибка TS rest parameters must be an array type? Но ваш ...iterables: { [I in keyof T]: Iterable<T[I]> } не является типом массива? Должен сказать, что я тоже не знаю, что там происходит с взаимодействиями I и T. Очень признателен, если вы сможете написать ответ, объясняющий шаги.

Robin De Schepper 30.03.2024 17:26

Гарантирован ли порядок кортежа? как это определяется?

Arash Jahan Bakhshan 30.03.2024 17:31

Да, я хочу, чтобы этот тип был заказан. Если на входе f(string[], number[]), на выходе должно быть Generator<[string, number][]>, но если это, например, f(number[], string[], any[]), то должно быть именно Generator<[number, string, any][]>

Robin De Schepper 30.03.2024 17:33

Это сопоставленный тип с массивом, который дает другой массив (я включил ссылку на документацию в свой ответ ниже)

jcalz 30.03.2024 17:37
Зод: сила проверки и преобразования данных
Зод: сила проверки и преобразования данных
Сегодня я хочу познакомить вас с библиотекой Zod и раскрыть некоторые ее особенности, например, возможности валидации и трансформации данных, а также...
Как заставить Remix работать с Mantine и Cloudflare Pages/Workers
Как заставить Remix работать с Mantine и Cloudflare Pages/Workers
Мне нравится библиотека Mantine Component , но заставить ее работать без проблем с Remix бывает непросто.
Угловой продивер
Угловой продивер
Оригинал этой статьи на турецком языке. ChatGPT используется только для перевода на английский язык.
TypeScript против JavaScript
TypeScript против JavaScript
TypeScript vs JavaScript - в чем различия и какой из них выбрать?
Синхронизация localStorage в масштабах всего приложения с помощью пользовательского реактивного хука useLocalStorage
Синхронизация localStorage в масштабах всего приложения с помощью пользовательского реактивного хука useLocalStorage
Не все нужно хранить на стороне сервера. Иногда все, что вам нужно, это постоянное хранилище на стороне клиента для хранения уникальных для клиента...
Что такое ленивая загрузка в Angular и как ее применять
Что такое ленивая загрузка в Angular и как ее применять
Ленивая загрузка - это техника, используемая в Angular для повышения производительности приложения путем загрузки модулей только тогда, когда они...
0
7
67
1
Перейти к ответу Данный вопрос помечен как решенный

Ответы 1

Ответ принят как подходящий

Я был бы склонен использовать следующую позывную:

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][]> по желанию.

Детская площадка, ссылка на код

Другие вопросы по теме