This is the second article about pipe-and-combine and will go more in depth and the newest feature added with version 0.7.x - using generics in pipes.
The problem
First, it helps to understand the problem. When you use a generic function you just fine what you need to handle, but maybe you just want to add something and the upcoming function needs a value from the beginning. By default, TypeScript does not follow the hole line when you define something like this:
type pipe = <
A, B, C, D
fu1: (data: A) => B,
fu2: (data: B) => C,
fu3: (data: C) => D
>(fu1, fu2, fu3) => (data: A) => D
If all these functions are generic like <T extends { n: number }>(data: T) => { ...data, b: !!data.n}
TypeScript will follow only one step. So A of fu1 is defined as {n: number}' and B as
{a: number, b: boolean}'. This is fine if the next function only needs stuff that is explicitly defined, but if you need something from fu1 in fu3 that is not mentioned in fu2 but is piped through it, TypeScript will tell you that this value does not exist.
The second problem is that you cannot access the generic value of any function to define the input more clearly.
If we know that all functions will merge all the time, we could do something like
type Pipe = <
A, B, C, D
fu1: (data: A) => B,
fu2: (data: B) => B & C,
fu3: (data: C) => B & C & D
>(fu1, fu2, fu3) => (data: A) => B & C & D
But then really all functions have to merge or the type will not match again.
G is the solution
Before you think in the wrong direction, G
does not stand for God, it is not that powerful...
Ok, back to what g
really does and why we need it: Logically, it is a merge function. It calls your function and merges the output with the given object.
//GMerge is the type returned by the function g
type GMerge<I, O> = ((data: I) => I & O) & {
Brand: "GMerge";
};
import { g } from pipe-and-combine;
g(<T extends { n: number }>(data: T) => ({ b: !!data.n}));
// The return value will be of type GMerge
// When executed, g will call your function and merge the result with the data passed to g.
Logically, the function is very simple, but the important part is the branding. With this, pipe-and-combine
knows how to handle it and how to define the type. So you will have the real type and not just the minimal one required by the function before.
How to remove values
Besides the g
function witch merge, there is an omit
and pick
that you can use directly in your pipe, but they are also generic and handled in a special way.
import {pipe, omit, pick} from 'pipe-and-combine';
// remove the attributes a and b from the object
pipe(omit('a', 'b'))
// Pick the attributes a and b from the object and return a object with those attributes
pipe(pick('a', 'b'))
Additional
There are several other functions already implemented that you can read about on the npm page, and more will be added in the future. As you can read in the first article on `pipe-and-combine', even as the library grows, by using pipeing instead of chaining, unused code could be shaken off (tree shaking).
Top comments (0)