Skip to content

Instantly share code, notes, and snippets.

@phunanon
Created August 9, 2022 15:01
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save phunanon/dc29c15f78074a4db312c803654952a9 to your computer and use it in GitHub Desktop.
Save phunanon/dc29c15f78074a4db312c803654952a9 to your computer and use it in GitHub Desktop.
Strongly types a pipeline of functions.
const pipe =
<Ctx, Ingress, A, B, C, Egress>(
...args:
| [(x: Ingress, ctx: Ctx) => Promise<Egress>]
| [
(x: Ingress, ctx: Ctx) => Promise<A>,
(x: A, ctx: Ctx) => Promise<Egress>
]
| [
(x: Ingress, ctx: Ctx) => Promise<A>,
(x: A, ctx: Ctx) => Promise<B>,
(x: B, ctx: Ctx) => Promise<Egress>
]
| [
(x: Ingress, ctx: Ctx) => Promise<A>,
(x: A, ctx: Ctx) => Promise<B>,
(x: B, ctx: Ctx) => Promise<C>,
(x: C, ctx: Ctx) => Promise<Egress>
]
) =>
async (context: Ctx, ingress: Ingress) => {
let data: any = ingress;
for (const effect of args) {
data = await effect(data, context);
}
return <Egress>data;
};
async function myTest() {
const affect = pipe(
async (x: string, ctx: string) => x + ctx,
async (x: string, ctx: string) => x + ctx,
async (x: string, ctx: string) => Number(x + ctx)
);
const ctx = "hello";
const data = "hi";
const egress = await affect(ctx, data);
}
@phunanon
Copy link
Author

phunanon commented Sep 9, 2022

A crazier version:

type Handler<Ingress, Ctx, Egress> = (x: Ingress, ctx: Ctx) => Promise<Egress>;
type PipeA<Ctx, Ingress, A, B, C, D, E, F, G, H, I, Egress> = [
  Handler<Ingress, Ctx, A>,
  ...([Handler<A, Ctx, Egress>] | PipeB<Ctx, A, B, C, D, E, F, G, H, I, Egress>)
];
type PipeB<Ctx, A, B, C, D, E, F, G, H, I, Egress> = [
  Handler<A, Ctx, B>,
  ...([Handler<B, Ctx, Egress>] | PipeC<Ctx, B, C, D, E, F, G, H, I, Egress>)
];
type PipeC<Ctx, B, C, D, E, F, G, H, I, Egress> = [
  Handler<B, Ctx, C>,
  ...([Handler<C, Ctx, Egress>] | PipeD<Ctx, C, D, E, F, G, H, I, Egress>)
];
type PipeD<Ctx, C, D, E, F, G, H, I, Egress> = [
  Handler<C, Ctx, D>,
  ...([Handler<D, Ctx, Egress>] | PipeE<Ctx, D, E, F, G, H, I, Egress>)
];
type PipeE<Ctx, D, E, F, G, H, I, Egress> = [
  Handler<D, Ctx, E>,
  ...([Handler<E, Ctx, Egress>] | PipeF<Ctx, E, F, G, H, I, Egress>)
];
type PipeF<Ctx, E, F, G, H, I, Egress> = [
  Handler<E, Ctx, F>,
  ...([Handler<F, Ctx, Egress>] | PipeG<Ctx, F, G, H, I, Egress>)
];
type PipeG<Ctx, F, G, H, I, Egress> = [
  Handler<F, Ctx, G>,
  ...([Handler<G, Ctx, Egress>] | PipeH<Ctx, G, H, I, Egress>)
];
type PipeH<Ctx, G, H, I, Egress> = [
  Handler<G, Ctx, H>,
  ...([Handler<H, Ctx, Egress>] | PipeI<Ctx, H, I, Egress>)
];
type PipeI<Ctx, H, I, Egress> = [Handler<H, Ctx, I>, Handler<I, Ctx, Egress>];

const pipe =
  <Ctx, Ingress, A, B, C, D, E, F, G, H, I, Egress>(
    ...args: PipeA<Ctx, Ingress, A, B, C, D, E, F, G, H, I, Egress>
  ) =>
  async (context: Ctx, ingress: Ingress) => {
    let data: any = ingress;
    for (const effect of args) {
      data = await effect(data, context);
    }
    return <Egress>data;
  };

async function myTest() {
  const affect = pipe(
    async (x: string, ctx: string) => x + ctx,
    async (x: string, ctx: string) => x + ctx,
    async (x: string, ctx: string) => Number(x + ctx)
  );
  const ctx = "hello";
  const data = "hi";
  const egress = await affect(ctx, data);
}

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment