Вариант 1: Простой asyncPipe
( детская площадка ):
type MaybePromise<T> = Promise<T> | T
function asyncPipe<A, B>(ab: (a: A) => MaybePromise<B>): (a: MaybePromise<A>) => Promise<B>
function asyncPipe<A, B, C>(ab: (a: A) => MaybePromise<B>, bc: (b: B) => MaybePromise<C>): (a: MaybePromise<A>) => Promise<C>
// extend to a reasonable amount of arguments
function asyncPipe(...fns: Function[]) {
return (x: any) => fns.reduce(async (y, fn) => fn(await y), x)
}
Пример:
const userHasEvenName = asyncPipe(getUserById, getName, countLetters, asyncIsEven);
// returns (a: MaybePromise<number>) => Promise<boolean>
Предостережение: Это всегда будет возвращать обещание, даже если все аргументы функции syn c.
Давайте попробуем сделать результат Promise
, если любая из функций asyn c, в противном случае возвращает результат syn c. Типы здесь раздуваются очень быстро, поэтому я просто использовал версию с одной перегрузкой (два аргумента функции).
function asyncPipe<A, B, C>(ab: (a: A) => B, bc: (b: Sync<B>) => C): < D extends A | Promise<A>>(a: D) => RelayPromise<B, C, D, C>
// extend to a reasonable amount of arguments
function asyncPipe(...fns: Function[]) {
return (x: any) => fns.reduce((y, fn) => {
return y instanceof Promise ? y.then(yr => fn(yr)) : fn(y)
}, x)
}
Я определил двух помощников: Sync
всегда даст вам resolved Тип обещания, RelayPromise
преобразует последний параметр типа в обещание, если какой-либо из других параметров является обещанием (дополнительную информацию см. На игровой площадке).
Пример:
const t2 = asyncPipe(getName, countLetters)(Promise.resolve({ displayName: "kldjaf" }))
// t2: Promise<number>
const t3 = asyncPipe(getName, countLetters)({ displayName: "kldjaf" })
// t3: number
Предостережение: если вам нужны оба syn c + asyn c в одном типе, это будет очень сложно, и вы должны тщательно его протестировать (в моем примере еще может быть несколько,, я пока использовал только простую версию) ).
Также, возможно, существует причина совместимости, почему fp-ts использует специальную версию pipe
, которая улучшает использование TypeScript слева направо Вывод параметров типа (это может быть важно и для вас).
Примечания
Наконец, вы должны решить, стоит ли иметь специальную версию asyncPipe
только для Обещания - больше видов и Реализация означает больше потенциальных ошибок.
В качестве альтернативы используйте простой pipe
с функторами или монадами в стиле функционального программирования. Например, вместо использования обещания вы можете переключиться на типы Task
или TaskEither
(см. Пример fp-ts).