Остальные параметры и параметризованные типы

Есть ли лучший способ справиться с отображением параметров типа в TypeScript?

export function $S(): Parser<[]>
export function $S<A>(fn: Parser<A>): Parser<[A]>
export function $S<A, B>(a: Parser<A>, b: Parser<B>): Parser<[A, B]>
export function $S<A, B, C>(a: Parser<A>, b: Parser<B>, c: Parser<C>): Parser<[A, B, C]>
export function $S<A, B, C, D>(a: Parser<A>, b: Parser<B>, c: Parser<C>, d: Parser<D>): Parser<[A, B, C, D]>
export function $S<A, B, C, D, E>(a: Parser<A>, b: Parser<B>, c: Parser<C>, d: Parser<D>, e: Parser<E>): Parser<[A, B, C, D, E]>
export function $S<A, B, C, D, E, F>(a: Parser<A>, b: Parser<B>, c: Parser<C>, d: Parser<D>, e: Parser<E>, f: Parser<F>): Parser<[A, B, C, D, E, F]>
export function $S<A, B, C, D, E, F, G>(a: Parser<A>, b: Parser<B>, c: Parser<C>, d: Parser<D>, e: Parser<E>, f: Parser<F>, g: Parser<G>): Parser<[A, B, C, D, E, F, G]>
export function $S<A, B, C, D, E, F, G, H>(a: Parser<A>, b: Parser<B>, c: Parser<C>, d: Parser<D>, e: Parser<E>, f: Parser<F>, g: Parser<G>, h: Parser<H>): Parser<[A, B, C, D, E, F, G, H]>
export function $S<A, B, C, D, E, F, G, H, I>(a: Parser<A>, b: Parser<B>, c: Parser<C>, d: Parser<D>, e: Parser<E>, f: Parser<F>, g: Parser<G>, h: Parser<H>, i: Parser<I>): Parser<[A, B, C, D, E, F, G, H, I]>
export function $S<A, B, C, D, E, F, G, H, I, J>(a: Parser<A>, b: Parser<B>, c: Parser<C>, d: Parser<D>, e: Parser<E>, f: Parser<F>, g: Parser<G>, h: Parser<H>, i: Parser<I>, j: Parser<J>): Parser<[A, B, C, D, E, F, G, H, I, J]>
export function $S(...terms: Parser<any>[]): Parser<any[]>

// Minimal definition of parser
interface ParseState {
  input: string
  pos: number
}

interface ParseResult<T> {
  nextPos: number,
  value: T,
}

interface Parser<T> {
  (state: ParseState): ParseResult<T> | undefined
}

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

Справочная информация, которая может иметь отношение к вопросу: Каждый синтаксический анализатор — это функция, которая принимает текущее состояние (входная строка, текущая позиция) и возвращает результат ParseResult, если он соответствует этой позиции, или undefined, если он не соответствует. ParseResult имеет значение и новую позицию для перехода к состоянию ввода.

Конечно, это возможно, но не могли бы вы сделать код минимальный воспроизводимый пример, дав какое-то определение для Parser? Особенно там, где вы показываете некоторые вызовы $S(), чтобы мы все могли убедиться, что предложенное решение ведет себя так, как ожидалось.

jcalz 15.03.2022 21:17

это работает, но, как предположил @jcalz, определение Parser может сломать его потенциально детская площадка

kellys 15.03.2022 21:18

"Правильный способ" сделать это - этот подход с использованием сопоставленных типов массивов/кортежей, но я хочу видеть здесь минимальный воспроизводимый пример.

jcalz 15.03.2022 21:20
Зод: сила проверки и преобразования данных
Зод: сила проверки и преобразования данных
Сегодня я хочу познакомить вас с библиотекой 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 для повышения производительности приложения путем загрузки модулей только тогда, когда они...
1
3
32
1
Перейти к ответу Данный вопрос помечен как решенный

Ответы 1

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

Вы можете использовать отображение кортежа/массива следующим образом:

declare function $S<T extends any[]>(
  ...terms: { [I in keyof T]: Parser<T[I]> }
): Parser<T>;

Когда вы используете сопоставить тип над типом массива/кортежа T (пока T является универсальный), например {[I in keyof T]: F<I>}, это приведет к другому типу массива/кортежа, где фактически будут итерироваться только числовые ключи индекса I (хотя есть ошибка в Майкрософт/TypeScript#27995, что немного усложняет задачу для разработчиков, но здесь это не проблема, поскольку Parser<XXX> допускает любой тип для XXX в вашем примере).

Итак, здесь T — это один тип, похожий на массив/кортеж, соответствующий различным параметрам типа в вашем примере (например, T будет [A, B, C] для вашей сигнатуры вызова с тремя аргументами). А { [I in keyof T]: Parser<T[I]> } — это еще один тип кортежа той же длины, что и T, где каждый тип элемента заключен в Parser<>. Итак, если T — это [string, number, boolean], то отображаемый тип — [Parser<string>, Parser<number>, Parser<boolean>].

Убедимся, что это работает:

declare const x: Parser<string>;
declare const y: Parser<number>;
declare const z: Parser<boolean>;
const xyz = $S(x, y, z);
// const xyz: Parser<[string, number, boolean]>

Выглядит неплохо!

Ссылка на код для игровой площадки

Спасибо! Это было очень полезно. Бонусный раунд... У меня также есть парсер «Выбор» $C вот так: ``` экспортировать функцию $C<A>(a: Parser<A>): Parser<A> экспортировать функцию $C<A, B>( a: Parser<A>, b: Parser<B>): Parser<A | B> функция экспорта $C<A, B, C>(a: Parser<A>, b: Parser<B>, c: Parser<C>): Parser<A | Б | C> ... ``` Существует ли аналогичный шаблон для выбора объединения значений типов?

Daniel X Moore 15.03.2022 22:13

Ну, вроде это, наверное. Обратите внимание, что дополнительные вопросы действительно принадлежат их собственному сообщению (или, если это важно, должны были быть в исходном вопросе), поскольку комментарии должны быть эфемерными, а вопросы SO предназначены для часто задаваемых вопросов для будущих читателей. (Кроме того, никто не получает эти сладкие очки репутации за комментарии)

jcalz 15.03.2022 22:18

Спасибо, мне нужно многое узнать о TypeScript, но эти ответы помогли мне перейти на следующий уровень.

Daniel X Moore 15.03.2022 22:31

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