Есть ли лучший способ справиться с отображением параметров типа в 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 имеет значение и новую позицию для перехода к состоянию ввода.
это работает, но, как предположил @jcalz, определение Parser может сломать его потенциально детская площадка
"Правильный способ" сделать это - этот подход с использованием сопоставленных типов массивов/кортежей, но я хочу видеть здесь минимальный воспроизводимый пример.






Вы можете использовать отображение кортежа/массива следующим образом:
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> ... ``` Существует ли аналогичный шаблон для выбора объединения значений типов?
Ну, вроде это, наверное. Обратите внимание, что дополнительные вопросы действительно принадлежат их собственному сообщению (или, если это важно, должны были быть в исходном вопросе), поскольку комментарии должны быть эфемерными, а вопросы SO предназначены для часто задаваемых вопросов для будущих читателей. (Кроме того, никто не получает эти сладкие очки репутации за комментарии)
Спасибо, мне нужно многое узнать о TypeScript, но эти ответы помогли мне перейти на следующий уровень.
Конечно, это возможно, но не могли бы вы сделать код минимальный воспроизводимый пример, дав какое-то определение для
Parser? Особенно там, где вы показываете некоторые вызовы$S(), чтобы мы все могли убедиться, что предложенное решение ведет себя так, как ожидалось.