В TS есть 2 варианта итерироваться по частям юнион типа - это условные типы и итерация по ключам, притом итерация по ключам ограничена типом
number | string | symbol
Условные типы проверяют условие для каждого варианта юниона, а значит позволяют трансформировать каждый из вариантов по отдельности:
type MyFunc = <N extends string>() => (N extends string ? Field<N> : never)[];
но возвращаемый тип так же будет юнионом:
({ name: "town" } | { name: "city" })[]
Сделать кортежный тип из юниона не выйдет, так как в юнионе не определен порядок, тип
"town" | "city" это тоже самое, что и тип
"city" | "town".
Но можно сделать кортеж из кортежа:
type MyFunc = <N extends string[]>() => {
[K in keyof N]: N[K] extends string ? Field<N[K]> : never;
};
myFunc<["town", "city"]>()
Ну и в реальном коде гораздо удобнее, когда дженерик выводится из аргументов, а для того что бы TS сам вывел кортежный тип из абстрактного массива, данный массив нужно помечать
as const, что так же делает его readonly массивом. Но принимать аргументы через readonly там где нам не нужно их мутирорвать - это вообще хорошая практика. Полный пример будет выглядеть так:
https://www.typescriptlang.org/play?#code/C4TwDgpg...