Typescript: Поддержка предложенного оператора конвейера ES Next "|>"

Созданный на 10 авг. 2017  ·  79Комментарии  ·  Источник: microsoft/TypeScript

ES Next Suggestion Waiting for TC39

Самый полезный комментарий

Сейчас на 1 стадии

Все 79 Комментарий

Мое любимое предложение :( В настоящее время мы действительно можем писать this бесплатных программ.

Для справки, предложение TC39: https://github.com/tc39/proposal-pipeline-operator

Не то, чтобы предложение еще даже не было на стадии 0. Если он когда-либо будет добавлен в семантику языка и другие детали, скорее всего, изменятся.

Я думаю, что это будет впервые (кроме некоторых старых вещей, таких как Enum и модульная система), но может ли машинописный текст, реализующий это, сделать его более заметным и повысить спрос на него в остальной части экосистемы ecma?

Просто хотел поделиться обходным путем для отсутствующего оператора конвейера, вдохновленным https://vanslaars.io/post/create-pipe-function/...

SyncPipe с синхронным сокращением

// SyncPipe with synchronous reduction
type SyncPipeMapper<T, U> = (data: T | U) => U;
type SyncPipeReducer<T, U> = (f: SyncPipeMapper<T, U>, g: SyncPipeMapper<T, U>) => SyncPipeMapper<T, U>;
type SyncPipe<T, U> = (...fns: SyncPipeMapper<T, U>[]) => SyncPipeMapper<T, U>;
function createSyncPipe<T, U>(): SyncPipe<T, U> {
    const syncPipe: SyncPipeReducer<T, U> = (f: SyncPipeMapper<T, U>, g: SyncPipeMapper<T, U>) => (data: T) => g(f(data));
    return (...fns: SyncPipeMapper<T, U>[]): SyncPipeMapper<T, U> => fns.reduce(syncPipe);
}

// Example:
function testSyncPipe(num: number): number {
    const addOne: SyncPipeMapper<number, number> = (data: number): number => {
        return data + 1;
    }
    const syncPipe: SyncPipe<number, number> = createSyncPipe();
    const syncWaterfall: SyncPipeMapper<number, number> = syncPipe(
        addOne,
        addOne,
        addOne,
    );

    // Does the equivalent of num+3
    const lastnumber: number = syncWaterfall(num);
    return lastnumber;
}

AsyncPipe с асинхронным сокращением

// AsyncPipe with asynchronous reduction
type AsyncPipeMapper<T, U> = (data: T | U) => Promise<U>;
type AsyncPipeReducer<T, U> = (f: AsyncPipeMapper<T, U>, g: AsyncPipeMapper<T, U>) => AsyncPipeMapper<T, U>;
type AsyncPipe<T, U> = (...fns: AsyncPipeMapper<T, U>[]) => AsyncPipeMapper<T, U>;
function createAsyncPipe<T, U>(): AsyncPipe<T, U> {
    const asyncPipe: AsyncPipeReducer<T, U> = (f: AsyncPipeMapper<T, U>, g: AsyncPipeMapper<T, U>) => async (data: T) => g(await f(data));
    return (...fns: AsyncPipeMapper<T, U>[]): AsyncPipeMapper<T, U> => fns.reduce(asyncPipe);
}

// Example:
async function testAsyncPipe(num: number): Promise<number> {
    const addOne: AsyncPipeMapper<number, number> = async (data: number): Promise<number> => {
        return data + 1;
    }
    const asyncPipe: AsyncPipe<number, number> = createAsyncPipe();
    const asyncWaterfall: AsyncPipeMapper<number, number> = asyncPipe(
        addOne,
        addOne,
        addOne,
    );

    // Does the equivalent of num+3
    const lastnumber: number = await asyncWaterfall(num);
    return lastnumber;
}

Труба с асинхронным редуктором (упрощенная)

Я использую это чаще всего:

// Pipes with asynchronous reduction
type PipeMapper<T> = (data: T) => Promise<T>;
type PipeReducer<T> = (f: PipeMapper<T>, g: PipeMapper<T>) => PipeMapper<T>;
type Pipe<T> = (...fns: PipeMapper<T>[]) => PipeMapper<T>;
function createPipe<T>(): Pipe<T> {
    const pipePipe: PipeReducer<T> = (f: PipeMapper<T>, g: PipeMapper<T>) => async (data: T) => g(await f(data));
    return (...fns: PipeMapper<T>[]): PipeMapper<T> => fns.reduce(pipePipe);
}

// Example:
async function testPipe(num: number): Promise<number> {
    const addOne: PipeMapper<number> = async (data: number): Promise<number> => {
        return data + 1;
    }
    const pipe: Pipe<number> = createPipe();
    const waterfall: PipeMapper<number> = pipe(
        addOne,
        addOne,
        addOne,
    );
    // Does the equivalent of num+3
    const lastnumber: number = await waterfall(num);
    return lastnumber;
}

Я надеюсь, что вы найдете это полезным!

@PublicParadise слишком шаблонный :p

Хотя я определенно хотел бы увидеть какой-то вариант этого оператора в языке, предполагаемая потребность в нем исходит из двух разных ограничений ECMAScript в его нынешнем виде.

Первую очень сложно обойти или даже решить на языке: невозможность расширять встроенные объекты гигиеничным образом.

Второй, однако, вообще не нуждается в поддержке языкового уровня и может быть исправлен: стандартную библиотеку можно назвать анемичной.

Максимально Минимум — это полный провал.

Почему требуются месяцы и месяцы споров, чтобы получить Array.prototype.flatMap в языке?

Это один метод, и он должен был быть там с самого начала, и должно быть очевидно, что его следует добавить.

Возможно, через 6 лет у Array.prototype будет метод groupBy .

К настоящему времени у него есть несколько реализаций Babel, которые, мы надеемся, помогут в предложении TC39:

Сейчас на 1 стадии

Итак, есть ли у этой красавицы шансы попасть в ТС? Это будет соответствовать F#. <3

Хотя есть исключения, когда предложение _важно_ для TypeScript и типов, предложения обычно не реализуются до тех пор, пока они не достигнут 3-го этапа TC39 в TypeScript, поскольку они недостаточно стабильны, чтобы гарантировать отсутствие значительных поломок и регрессий.

Хотя никто из основной команды еще не прокомментировал эту проблему, на мой взгляд, она была бы недостаточно _важной_, чтобы рассматривать ее для реализации до этапа 3. Лучше всего сосредоточиться на поддержке лидера и предложения на TC39.

Если бы только у TS была возможность просто передать этот оператор, чтобы позволить вавилону с плагинами справиться с ним.
Или иметь собственные плагины синтаксиса, как это делают post-css. Несколько лет ожидания примитивного оператора — это слишком.

@garkin : Проблема здесь в том, что TS должен понимать код, чтобы выполнять свою работу по обеспечению безопасности типов, что плохо сочетается со случайным кодом, который он не понимает. Если бы не получение макросов (#4892), в этом случае он просто скомпилировал бы код, который понимает. Но я бы пока не ожидал этого в дорожной карте, так как довольно много элементов стандартной библиотеки по-прежнему сложно набирать atm.

Теперь, когда Babel понимает машинописный текст, вы можете запустить его через Babel.
машинопись

26 октября 2017 г., 19:01, «Tycho Grouwstra» [email protected] написал:

@garkin https://github.com/garkin : проблема здесь в том, что TS должен
понимать код, чтобы выполнять свою работу по обеспечению безопасности типов, что не
хорошо сочетается со случайным кодом, который он не понимает. Если бы не получить
макросы (#4892 https://github.com/Microsoft/TypeScript/issues/4892 ), в
в этом случае он просто скомпилирует код, который он понимает. Но я бы не стал
ожидать, что в дорожной карте совсем еще нет, так как довольно много битов стандарта
библиотеке по-прежнему сложно набирать atm.


Вы получаете это, потому что вы создали тему.
Ответьте на это письмо напрямую, просмотрите его на GitHub
https://github.com/Microsoft/TypeScript/issues/17718#issuecomment-339748284 ,
или заглушить тему
https://github.com/notifications/unsubscribe-auth/AAZQTO6UiVHbrM6SRwaBhm8obaa3R7e9ks5swMkCgaJpZM4OzVEg
.

Теперь, когда Babel понимает машинописный текст, вы можете запустить его через Babel.
машинопись

Хотя время сборки вдвое больше :p

Было бы неплохо, если бы typescript был просто плагином Babel, тогда вам не нужно было бы
пропустить через обе программы

26 октября 2017 г., 20:16, AlexGalays [email protected] написал:

Теперь, когда Babel понимает машинописный текст, вы можете запустить его через Babel.
машинопись

Хотя время сборки вдвое больше :p


Вы получаете это, потому что вы создали тему.
Ответьте на это письмо напрямую, просмотрите его на GitHub
https://github.com/Microsoft/TypeScript/issues/17718#issuecomment-339769856 ,
или заглушить тему
https://github.com/notifications/unsubscribe-auth/AAZQTEArBw8jj0BcZFM2yLj5ErfbtNrgks5swNqagaJpZM4OzVEg
.

@graingert : это хороший вариант, я
К сожалению, он не будет работать с API Language Service машинописного текста, который используется VisualStudioCode, Webstorm и другими IDE.

Что касается «плагинов TS», можно легко достичь желаемого результата, скажем, с помощью простого (предварительного) транспилятора для оператора канала, который понимает синтаксис TS и создает строго типизированный эквивалент оператора. Он отлично скомпилируется с проверкой типов и еще чем-то.

Конфигурация веб-пакета для этого может выглядеть примерно так:

module: {
        rules: [
            { test: /\.ts$/, loader: 'ts-pipe-operator', enforce: 'pre' },
            { test: /\.ts$/, loader: 'ts-loader' },
            ...
        ]
 }

Единственная проблема, как указал @garkin , заключается в том, что служба TS не сможет сопоставить переданные части с исходным исходным файлом, тогда IDE, использующие службу, не будут работать должным образом, даже если они уже распознают оператор (ES Next синтаксис включен или что-то в этом роде).

Возможно, если мы создадим NFR (или, может быть, уже есть?) для службы TS для поддержки кумулятивных исходных карт, которые будут применяться между исходным файлом и переданным результатом, который передается компилятору, этот и другие плагины будут возможны, не затрагивая остального сообщества и, что наиболее важно, без усложнения работы основной команды.


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


И последнее, но не менее важное: может ли кто-нибудь указать мне на _официальную_ ветку (если таковая имеется) относительно такого рода плагинов?

Должен сказать, что я действительно ценю спокойный способ представления новых предложений и предпочитаю монолитный инструментарий Typescript и LessCSS гораздо больше, чем олимпиаду специальных плагинов Flow+Babel и Post-CSS.

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

Трубщик — это как наркотик для входа в функциональный мир, он заставляет меня говорить и желать странных вещей.

Итак, #14419 и даже некоторые полезные практические выводы уже есть. Не должно быть сложно интегрировать те, у кого есть ts-loader .

Интеграция трансформеров tsconfig.json (и, следовательно, API языковой службы, а не только настроенная tsc ) № 14654 была отклонена _в краткосрочной перспективе_.

11976 обсуждает плагины языковой службы, которые выглядят как инструменты только для линтинга.

16607, предлагающий расширение этих плагинов для трансформаторов.

@PublicParadise или просто используйте flow lodash или pipe Rambda?

в любом случае, было бы здорово увидеть поддержку в TS. Мне нравятся функциональные шаблоны, которые поддерживает JS (особенно с выводом типа TS), но некоторые шаблоны не очень хорошо читаются. Это было бы огромно, поскольку большие библиотеки TS, такие как RxJS и IxJS, движутся к бесточечной функциональной композиции вместо расширения/наследования прототипа, это значительно улучшает встряхивание дерева и поддержку пользовательских операторов.

@felixfbecker Ты имеешь в виду pipe Ramda? Мне нужно попробовать еще раз, но исторически сложилось так, что ramda была библиотекой JS-first, она очень динамична и сложна для ввода (например, lodash), что усугубляется тем фактом, что у TS было много проблем с выводом из возвращаемых значений функции (возможно, это было исправил недавно, но не уверен)
Я не использую lodash как он плохо спроектирован, смешивая изменяемые и неизменяемые функции в одном большом пространстве имен.

Это на самом деле работает прилично, если ваши функции и цепочки не супер сумасшедшие:

https://github.com/DefinitelyTyped/DefinitelyTyped/blob/b67c928904f03d0911c99ab938b14bc2e59cad40/types/lodash/index.d.ts#L7819 -L7855

Это на самом деле работает прилично, если ваши функции и цепочки не супер сумасшедшие.

Позвольте мне уточнить, что это «не супер сумасшедший»: все ломается, если ваши функции имеют дженерики (см. https://github.com/types/npm-ramda/issues/86), например, R.pipe(R.identity) .

Кроме того, давайте проясним, предложение относится к Этапу 1. Основная команда становится еще более застенчивой в представлении вещей до Этапа 3. Декораторы являются частью примера. Несмотря на то, что они были помечены как _экспериментальные_, мы все пошли дальше, включили этот флаг и написали весь наш производственный код, используя их. Предложение теперь отскочило, и есть некоторые фундаментальные критические изменения в синтаксисе и семантике, которые будут означать, что нам всем придется реорганизовать наш код, что ставит основную команду в затруднительное положение, потому что, если они _только_ поддерживают окончательный синтаксис, чем у всех, ломается в тот день, когда они его выпускают, или если они сохраняют устаревшие вещи, другие изменения в компиляторе могут затруднить поддержку двух синтаксисов, и в конечном итоге вы захотите избавиться от старых вещей, но когда .. 💥 💥

Таким образом, лучшее, что можно сделать с функциями, основанными на стандартах, — это не обсуждать здесь поддержку или отсутствие поддержки TypeScript, а найти дружелюбного местного представителя TC39 и заявить, что эта функция действительно важна для вас, а также принять участие в обсуждение предложения, указанное выше, на GitHub. Чем быстрее будет разрешена семантика и чем быстрее она дойдет до стадии 3, тем быстрее у всех нас появятся хорошие вещи!

Теперь, когда в rxjs есть допустимые операторы, это было бы еще более замечательной функцией в Typescript.
https://github.com/ReactiveX/rxjs/blob/master/doc/lettable-operators.md

Можем ли мы попросить кого-нибудь из команды TS пролить свет на этот запрос?

Они пометили это ES Next и Suggestion ... Я могу процитировать вам главу и стих из других мест, где они прокомментировали предложения ES Next, а также когда и как они их реализуют ...

Комментарий от них ничего не изменит. Как вы думаете, они тайно работают над этим за кулисами, ожидая, когда это станет известно сообществу? Часто не вступают в дело, если нечего добавить... К уже сказанному добавить нечего.

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

@aminpaks Мне не очень нравится эта идея, так как она может быстро привести к бабелификации (как в Вавилонской башне, а не в превосходном транспиляторе Babeljs 😀)

Поскольку плагины подразумевают поведение на уровне типов, становится трудно понять смысл программ, а обработка зависимостей на уровне исходного кода потребует ряда сложных, но очень полезных функций, которых в настоящее время нет в TypeScript.

Как бы я ни хотел, чтобы это было везде, я рад, что TS придерживается более сдержанного подхода к реализации новых функций и придерживается стандартов. Эта позиция делает TS в целом более привлекательным для таких людей, как я, которые глубоко заботятся об отклонении от стандарта, но наслаждаются расширенными функциями, предоставляемыми транспиляцией до принятия браузером поставщика / JS-движка (но не обязательно до стандартизации). Это тонкий баланс.

Лично я не в пользу оператора конвейера в ущерб оператору привязки. Это сводится к читабельности на самом деле.

Данный:

function* where<T>(items: Iterable<T>, predicate: (item:T)=>boolean){
  for (let item of items){
    if(predicate(item)) yield item;
  }
}

привязка-операция:

[1,2,3]::where(x=>x>2)

конвейерная операция:

[1,2,3]|>(_)=>where(_,x=>x>2)

ts-team правы, не беспокоясь до стадии 3; и даже в этом случае предложения этапа 3 могут быть отозваны (например, SIMD). Жаль, что у нас не может быть плагинов — «риск» перекладывается на отдельного разработчика. Существует множество других вариантов использования плагинов, не в последнюю очередь для поддержки таких вещей, как файлы .vue содержащие машинописный текст.

@MeirionHughes Я согласен. Хотя я предпочел бы иметь оператор конвейера, а не вообще никакого сахара, он вдохновлен языками, в которых функции автоматически каррируются и где библиотеки построены вокруг использования этого. Также предполагается, что методы конвейера _не_ являются членами значений конвейера.

Так что тогда у вас было бы

function where<T>(predicate: (item: T) => boolean): (items: Itererable<T>) => Itererable<T> {
  return function* () {
    for (const item of items) if (predicate(item)) yield item;
  };
}

function select<T, R>(projection: (item: T) => R): (items: Itererable<T>) => Itererable<R> {
  return function* () {
    for (const item of items) yield projection(item);
  };
}

а то бы ты написал

[1, 2, 3] |> where(x => x > 2) |> select(x => x ** 2);

но поскольку JavaScript не выполняет и не может выполнять функции автоматического каррирования, кажется, что он хорошо работает только с библиотеками, разработанными с учетом каррирования.

Я могу ошибаться в этом, я не очень в курсе этого предложения.

Тогда новая библиотека, которая по умолчанию предоставляет каррированные функции утилиты, может стать новым стандартом: p

@AlexGalays Я думаю, что это возможно, если это пройдет. Пока это не является частью подрывного заговора по превращению JavaScript в OCaml, все в порядке.

@MeirionHughes ваш пример where вообще не использует this , поэтому оператор привязки не будет работать. У оператора привязки также есть много открытых вопросов по безопасности типов. Разрешено ли where получать доступ к частным свойствам из this как метод класса? Если нет, то какой смысл использовать this ? Если да, то изменение приватных свойств внезапно становится переломным изменением, которое полностью противоречит цели приватных свойств.
Вы также заявляете, что синтаксис менее удобочитаем, но, например, опускаете скобки в своем примере с оператором привязки, но добавляете ненужные скобки в примере с конвейером. И оператор конвейера, конечно, вообще не будет работать с функциями, написанными для связывания, но он будет хорошо работать с каррированными функциями, такими как операторы rxjs, ramda или lodash/fp.

@aluanhaddad Помимо всех библиотек fp, RxJS является примером широко используемой библиотеки, которая перешла от операторов на прототипах (у которых много проблем, в основном связанных с встряхиванием деревьев и безопасностью общих типов) к операторам в виде каррированных функций. Большинство библиотек, вероятно, не выбирают этот путь прямо сейчас, потому что у нас нет хорошего синтаксиса для него.

@felixfbecker, вы правы, и пример @aluanhaddad, в который вложены результирующие функции, которые

Кто-нибудь думал или внедряет собственный преобразователь, чтобы получить поддержку конвейера немного раньше? Судя по всему, это можно сделать с помощью пользовательского преобразователя, который просто транспилирует _просто_ саму часть конвейера. затем вы можете выполнить его через: https://github.com/cevek/ttypescript

Это возможно? Использование пользовательских преобразований для использования синтаксиса, поддерживаемого Babel, при сохранении работоспособности таких вещей, как инструменты TypeScript?

Может быть? есть предустановка, которая работает только с предложениями 0-2: https://www.npmjs.com/package/babel-preset-proposal-typescript — это означает, что вы вставляете ее перед отправкой в ​​машинопись. Тем не менее, вы intellisense, скорее всего, будете скучать. Однако с https://github.com/cevek/ttypescript#visual -studio-code вам это может сойти с рук.

@MeirionHughes Я рад, что был полезен ❤️.
Теперь нам просто нужен #6606, чтобы мы могли преобразовать методы произвольных прототипов в каррированные функции!

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

Я добавил собственную реализацию для TS # 22816.

Как один из тех, кто настаивает на операторе конвейера, я умоляю вас: пожалуйста, _не_ реализуйте это в TypeScript, пока он не продвинется дальше. Мы все еще обсуждаем два возможных предложения, которые принципиально несовместимы друг с другом, поэтому TypeScript будет очень жалко, если реализует это слишком рано.

Если вы заинтересованы в этом предложении, взгляните на репозиторий здесь и примите участие: https://github.com/tc39/proposal-pipeline-operator/ Мы будем рады вашим отзывам! И мы работаем над плагинами Babel для различных предложений, так что у вас будет возможность опробовать их в своих (не TypeScript) проектах.

Но предложение еще не готово к использованию чего-то вроде TypeScript.

Мы определенно не объединяем это.

Можем ли мы иметь что-то вроде https://github.com/babel/babel-eslint , что позволит нам продолжать использовать функции, поддерживаемые Babel, и обеспечить работу проверки типов после того, как функции, не поддерживаемые TypeScript, будут удалены?

@masaeedu Да! Этот

@MeirionHughes с предложением частичной заявки становится проще:

[1,2,3] |> where(?, x=>x>2)

@bernatmv : между прочим, есть что-то близкое к этому, что работает сегодня.

@tycho01 Tycho01 Но не в TypeScript, пока он не наберет 2,8 набора: https://github.com/DefinitelyTyped/DefinitelyTyped/issues/25067

@jeremejevs @bernatmv на самом деле, R.__ было набрано с помощью codegen в npm-ramda . Лучшие способы использования 2.8 приветствуются!

Я новичок в Javascript и TypeScript (2 недели), поэтому, пожалуйста, простите меня, если есть более простое решение. Но ниже то, что я придумал в отсутствие оператора конвейера. Первоначально я пытался использовать несколько перегрузок pipe которые работали с параметрами типа 2, 3, 4 и т. д., но не мог понять, как заставить разрешение перегрузки TypeScript работать так же, как в C#. У нас могут быть разные функции pipe1<A,B> , pipe2<A,B,C> и pipe3<A,B,C,D> но с ними будет сложно работать, так как вам придется выбирать имя функции на основе количества аргументов, которые вы используете. хотел. Есть ли более простое безопасное решение, чем то, что я предложил ниже? Существует ли определение рекурсивного типа, которое может принимать неограниченное количество параметров? Правильно ли я использую типы условий?

type LastOf<
    A,
    B=never,
    C=never,
    D=never,
    E=never,
    F=never,
    G=never,
    H=never,
    I=never,
    J=never> =
    [B] extends [never] ? A :
    [C] extends [never] ? B :
    [D] extends [never] ? C :
    [E] extends [never] ? D :
    [F] extends [never] ? E :
    [G] extends [never] ? F :
    [H] extends [never] ? G :
    [I] extends [never] ? H :
    [J] extends [never] ? I :
    J;

export function pipe<A, B, C=never, D=never, E=never, F=never, G=never, H=never, I=never, J=never>(
    a: A,
    mapA: (a: A) => B,
    mapB?: (b: B) => C,
    mapC?: (c: C) => D,
    mapD?: (d: D) => E,
    mapE?: (e: E) => F,
    mapF?: (f: F) => G,
    mapG?: (g: G) => H,
    mapH?: (h: H) => I,
    mapI?: (i: I) => J
): LastOf<A, B, C, D, E, F, G, H, I, J> {
    if (mapB === undefined) {
        return mapA(a) as LastOf<A, B, C, D, E, F, G, H, I, J>;
    }
    if (mapC === undefined) {
        return mapB(mapA(a)) as LastOf<A, B, C, D, E, F, G, H, I, J>;
    }
    if (mapD === undefined) {
        return mapC(mapB(mapA(a))) as LastOf<A, B, C, D, E, F, G, H, I, J>;
    }
    if (mapE === undefined) {
        return mapD(mapC(mapB(mapA(a)))) as LastOf<A, B, C, D, E, F, G, H, I, J>;
    }
    if (mapF === undefined) {
        return mapE(mapD(mapC(mapB(mapA(a))))) as LastOf<A, B, C, D, E, F, G, H, I, J>;
    }
    if (mapG === undefined) {
        return mapF(mapE(mapD(mapC(mapB(mapA(a)))))) as LastOf<A, B, C, D, E, F, G, H, I, J>;
    }
    if (mapH === undefined) {
        return mapG(mapF(mapE(mapD(mapC(mapB(mapA(a))))))) as LastOf<A, B, C, D, E, F, G, H, I, J>;
    }
    if (mapI === undefined) {
        return mapH(mapG(mapF(mapE(mapD(mapC(mapB(mapA(a)))))))) as LastOf<A, B, C, D, E, F, G, H, I, J>;
    }
    return mapI(mapH(mapG(mapF(mapE(mapD(mapC(mapB(mapA(a))))))))) as LastOf<A, B, C, D, E, F, G, H, I, J>;
}

test("map once", () => {
    const result = pipe(
        2,
        i => i * 10);
    expect(result).toBe(20);
});

test("map twice", () => {
    const result = pipe(
        2,
        i => i * 10,
        i => `the answer is ${i}`);
    expect(result).toBe('the answer is 20');
});

test("map three times", () => {
    const result = pipe(
        2,
        i => i * 10,
        i => -i,
        i => ({ a: i, b: -i }));
    expect(result).toEqual({ a: -20, b: 20 });
});

Я думал, что _.flow lodash/fp уже напечатал это?

В среду, 9 мая 2018 г., 22:19 jmagaram, уведомления@github.com написал:

Я немного новичок в Javascript и TypeScript (2 недели), поэтому, пожалуйста, простите
меня, если есть более простое решение. Но ниже то, что я придумал
с при отсутствии оператора трубопровода. Я изначально пытался иметь
многократные перегрузки трубы, которые работали с 2, 3, 4 и т.д. типом
параметры, но не мог понять, как получить перегрузку TypeScript
разрешение для работы, как в C#. У нас могут быть разные функции
pipe1 , pipe2 и pipe3 , но это было бы сложно работать, так как вам придется выбирать имя функции в зависимости от того, сколькоаргументы, которые вы хотели.
Существует ли определение рекурсивного типа, которое может приниматьнеограниченное количество параметров?

введите LastOf = [B] расширяет [никогда]?
Б :[D] расширяет [никогда]?
Д:[F] расширяет [никогда] ?

функциональная труба ( а: А,карта А: (а: А) => В,mapB?: (b:B) => C,mapC?: (c:C) => D,mapD?:(d:D) => E,mapE?: (e:E) => F): LastOf { const b = карта А (а);переключатель (картаB) {case undefined: вернуть b как LastOf ; дефолт: {const c = карта B (b);переключатель (mapC) {case undefined: вернуть c как LastOf ; дефолт: {const d = карта C (с);переключатель (mapD) {case undefined: вернуть d как LastOf ; дефолт: {const е = mapD(d);переключатель (карта) {case undefined: вернуть e как LastOf ;по умолчанию: вернуть mapE(e) как LastOf ; }}}}}}}}

test("сопоставить один раз", () => {
константный результат = труба (
2,
я => я * 10);
ожидать(результат).быть(20);
});

test("отобразить дважды", () => {
константный результат = труба (
2,
я => я * 10,
я => the answer is ${i} );
ожидаем(результат).toBe('ответ равен 20');
});

test("отобразить три раза", () => {
константный результат = труба (
2,
я => я * 10,
я => -я,
я => ({ а: я, б: -я }));
ожидать(результат).toEqual({a: -20, b: 20});
});


Вы получаете это, потому что вас упомянули.
Ответьте на это письмо напрямую, просмотрите его на GitHub
https://github.com/Microsoft/TypeScript/issues/17718#issuecomment-387878691 ,
или заглушить тему
https://github.com/notifications/unsubscribe-auth/AAZQTLm6LrWe5KVx4aGBFUd4yRUHkkrZks5tw11cgaJpZM4OzVEg
.

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

@jmagaram все TS обычно облегчают задачу благодаря умозаключениям, но если это работает, то работает.

@jmagaram Несколько более простая альтернатива машинописному тексту может выглядеть примерно так.

interface IPipe<T> {
    readonly value: () => T;
    chain<R>(fn: (x: T) => R): IPipe<R>;
}

function pipe<T>(val: T): IPipe<T> {
    return {
        chain: fn => pipe(fn(val)),
        value: () => val
    };
}

Использование по-прежнему будет довольно чистым и строго типизированным.

pipe(["Hello", "There"])
    .chain(map(x => `${x}!`))
    .chain(xs => {
        ...
    })
    .value()

Я был бы очень признателен за возможность добавлять пользовательские операторы. В F# для этого есть хороший подход.

А пока вот более простой подход без упаковки:

function pipe<T1>(first:T1):T1
function pipe<T1, T2>(first:T1, second:(a:T1) => T2):T2
function pipe<T1, T2, T3>(first:T1, second:(a:T1) => T2, third:(a:T2) => T3):T3
function pipe<T1, T2, T3, T4>(first:()=>T1, second:(a:T1)=>T2, third:(a:T2)=>T3, fourth:(a:T3)=>T4):T4
function pipe<T1, T2, T3, T4, T5>(first:()=>T1, second:(a:T1)=>T2, third:(a:T2)=>T3, fourth:(a:T3)=>T4, fifth:(a:T4)=>T5):T5
function pipe<T1, T2, T3, T4, T5, T6>(first:()=>T1, second:(a:T1)=>T2, third:(a:T2)=>T3, fourth:(a:T3)=>T4, fifth:(a:T4)=>T5, sixth:(a:T5)=>T6):T6
function pipe<T1, T2, T3, T4, T5, T6, T7>(first:()=>T1, second:(a:T1)=>T2, third:(a:T2)=>T3, fourth:(a:T3)=>T4, fifth:(a:T4)=>T5, sixth:(a:T5)=>T6, seventh:(a:T6)=>T7):T7
function pipe<T1, T2, T3, T4, T5, T6, T7, T8>(first:()=>T1, second:(a:T1)=>T2, third:(a:T2)=>T3, fourth:(a:T3)=>T4, fifth:(a:T4)=>T5, sixth:(a:T5)=>T6, seventh:(a:T6)=>T7, eigth:(a:T7)=>T8):T8
function pipe<T1, T2, T3, T4, T5, T6, T7, T8, T9>(first:()=>T1, second:(a:T1)=>T2, third:(a:T2)=>T3, fourth:(a:T3)=>T4, fifth:(a:T4)=>T5, sixth:(a:T5)=>T6, seventh:(a:T6)=>T7, eigth:(a:T7)=>T8, ninth:(a:T8)=>T9):T9
function pipe<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10>(first:()=>T1, second:(a:T1)=>T2, third:(a:T2)=>T3, fourth:(a:T3)=>T4, fifth:(a:T4)=>T5, sixth:(a:T5)=>T6, seventh:(a:T6)=>T7, eigth:(a:T7)=>T8, ninth:(a:T8)=>T9, tenth:(a:T9)=>T10):T10
function pipe(first:any, ...args:Function[]):any {
    return (
        args && args.length 
        ? args.reduce(
            (result, next) => next(result),
            first instanceof Function ? first() : first
        )
        : first instanceof Function ? first() : first
    );
}

Это дает:
ts-pipe-example
( для получения дополнительной информации см. здесь )

Тем не менее, @graingert +1 вы правы: у lodash уже есть это для композиции (но не для канала):

const getUpperName = 
   _.flow(
      (p: Person) => `${p.FirstName} ${p.LastName}`,
      (s: string) => s.toUpper()
   )

В качестве альтернативы вы можете вместо этого добавить канал в Object.prototype:

Object.prototype.pipe = function<Self, Result>(this:Self, next:(value:Self) => Result):Result {
    return next(this)
}

Вместо этого это позволяет:
capture
( для получения дополнительной информации см. здесь )

Надеюсь, это поможет другим!

Он попал в Firefox под флагом компиляции --enable-pipeline-operator .

https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Pipeline_operator

Минута молчания павшему герою, оператору бинда :: , закрытому в пользу злого |> 😢

Ну, я думаю, это в глазах смотрящего, так как я предпочитаю |> :D

привет королю!

И тут я подумал, что это несбыточная мечта

Pipeline — это, по сути, простой вариант использования Identity Monad. Кроме того, pipe обычно равно compose в обратном порядке, тогда как pipeline больше похоже на pipe который вызывается сразу.

В любом случае, с нетерпением жду возможности увидеть это в Typescript.

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

Пт, 10 августа 2018 года, 2:53 Бабак [email protected] писал:

Pipeline — это, по сути, простой вариант использования Identity Monad. Также,
труба обычно составляется в обратном порядке, тогда как конвейер больше похож на трубу
который вызывается сразу.

В любом случае, с нетерпением жду возможности увидеть это в Typescript.


Вы получаете это, потому что вы прокомментировали.
Ответьте на это письмо напрямую, просмотрите его на GitHub
https://github.com/Microsoft/TypeScript/issues/17718#issuecomment-411824741 ,
или заглушить тему
https://github.com/notifications/unsubscribe-auth/AFXUx5rwM9wVrpAkHK2BNYkyy74HtWU5ks5uPGkNgaJpZM4OzVEg
.

--

Инфиксные функции ftw

Чт, 9 августа 2018 г., 23:35 Бен Битти-Худ, уведомления@github.com
написал:

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

Пт, 10 августа 2018 года, 2:53 Бабак [email protected] писал:

Pipeline — это, по сути, простой вариант использования Identity Monad. Также,
труба обычно составляется в обратном порядке, тогда как конвейер больше похож на трубу
который вызывается сразу.

В любом случае, с нетерпением жду возможности увидеть это в Typescript.


Вы получаете это, потому что вы прокомментировали.
Ответьте на это письмо напрямую, просмотрите его на GitHub
<
https://github.com/Microsoft/TypeScript/issues/17718#issuecomment -411824741
,
или заглушить тему
<
https://github.com/notifications/unsubscribe-auth/AFXUx5rwM9wVrpAkHK2BNYkyy74HtWU5ks5uPGkNgaJpZM4OzVEg

.

--


Вы получаете это, потому что вас упомянули.
Ответьте на это письмо напрямую, просмотрите его на GitHub
https://github.com/Microsoft/TypeScript/issues/17718#issuecomment-411919587 ,
или заглушить тему
https://github.com/notifications/unsubscribe-auth/AAZQTHHFbVY5uGCWl-La_P-HF7UN6xPsks5uPLk8gaJpZM4OzVEg
.

идея инфиксных функций для машинописного текста почти так же стара, как и машинописный текст: https://github.com/Microsoft/TypeScript/issues/2319

Я знаю, что многие люди очень хотят этого, но я считаю, что TypeScript не должен реализовывать никаких дополнительных операторов, пока они не находятся на этапе 3. Все может измениться, и, конечно, есть некоторые исключения.

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

@BenBeattieHood Мы внедряем это в Babel, поэтому вы сможете протестировать его там. Если вы тестируете его в преобразователе компилятора, обязательно взгляните на текущие предложения , поскольку мы рассматриваем несколько форм оператора конвейера.

Я думаю, что нужно много думать о том, как его использовать; особенно в отношении ввода таких вещей, как:

function where<T>(predicate: (x: T) => boolean) {
  return function* (items: Iterable<T>): Iterable<T> {
    for (const item of items) {
      if (predicate(item)) {
        yield item;
      }
    }
  };
}

[1, 2, 3] |> where(x=>x> 1)

на данный момент с where(x => x > 1)([1,2,3]) он не может сделать вывод, что такое x. приведенное выше - одна из причин, по которой я надеялся, что операция :: победит, потому что (на первый взгляд) машинописному тексту кажется намного проще сделать вывод, что такое this

Или мы можем увидеть это по-другому: если он будет выпущен, он отдаст приоритет некоторым проблемам вывода, которые есть у TS 👍

Если вы следите за новостями спецификации и Babel, спецификация еще не установлена. Есть 2 предложения. Я уверен, что команда typescript добавит поддержку, когда спецификация будет завершена.

Инфиксные функции ftw

iirc JS называет эти «методы».

iirc JS называет эти «методы»

@ tycho01 Ваш комментарий, вероятно,

@BenBeattieHood Мы внедряем это в Babel, поэтому вы сможете протестировать его там. Если вы протестируете его в преобразователе компилятора, обязательно взгляните на текущие предложения, поскольку мы рассматриваем несколько форм оператора конвейера.

Парсер Babel теперь поддерживает предложение интеллектуального конвейера.

https://github.com/babel/babel/pull/8289

Любые обновления?

Любые обновления?

🤦‍♂️ TypeScript не реализует предложение, пока не достигнет этапа 3. Оператор конвейера в настоящее время находится на этапе 1 и имеет серьезные проблемы. Эта информация была предоставлена ​​несколько раз в этой теме.

пример серьезных проблем, пожалуйста?

может быть...

⚠ Предупреждение. Детали синтаксиса конвейера в настоящее время не решены. На рассмотрении находятся два конкурирующих предложения.

Да, это то, что я считаю серьезной проблемой.

Собираюсь заблокировать этот, так как все потоки в состоянии Ожидание TC39, как правило, просто идут по кругу.

Пинг!

Была ли эта страница полезной?
0 / 5 - 0 рейтинги