Reputation: 1345
Problem
I want to create a function, alongside a typing for said function, that allows me to divide an array into a tuple containing a variable number of typed arrays. The idea is call .divide on an array instance, and provide a variable number of typeguards/typeguard like functions, the output of divide would be a Tuple containing the same number of typed arrays as typeguards provided. If items do not fit any provided typeguard, they are discarded from the result. Sample usage:
const [typeAs, typeBs, typeCs] = myArr.divide(isTypeA, isTypeB, isTypeC);
const [typeFs] = myArr.divide(isTypeF)
const [typeAs, typeFs, typeDs, typeCs, typeXYZs] = myArr.divide(isTypeA,isTypeF,isTypeD,isTypeC,isTypeXYZ)
//etc
As a starting point I've attempted to create a function that divides to 3 vs. a variable length Tuple + args list. I've thrown together the following on an interface for Array:
type TGuard<S> = ((input: any) => input is S) | ((input: any) => boolean);
divide<OrigType, NewT1, NewT2, NewT3>(
t1: TGuard<NewT1>,
t2: TGuard<NewT2>,
t3: TGuard<NewT3>,
): [NewT1[], NewT2[], NewT3[]];
and an implementation:
Array.prototype.divide = function <OGType, NewT1, NewT2, NewT3>(
t1: TGuard<NewT1>,
t2: TGuard<NewT2>,
t3: TGuard<NewT3>,
): [NewT1[], NewT2[], NewT3[]] {
const d3: [NewT1[], NewT2[], NewT3[]] = [[], [], []];
const discards: OGType[] = [];
this.forEach((item) => {
t1(item) ? d3[0].push(item) : t2(item) ? d3[1].push(item) : t3(item) ? d3[2].push(item) : discards.push(item);
});
return d3;
};
This works but I'm at a loss on expanding this to accept a variable number of typeguards, and produce a variable number of typed arrays in a Tuple wrapper. I have several ideas for the eventual implementation, At present the main challenge I'm facing is the type signature
Upvotes: 0
Views: 36
Reputation: 26344
If we define divide
like this:
function divide<T, G extends (((item: T) => item is any) | ((item: T) => boolean))[]>(array: T[], ...guards: G): InferGuards<G>;
then we can map over G
and infer the guard types:
type InferGuards<G> = {
[K in keyof G]: G[K] extends (item: any) => item is infer U ? U[] : unknown[];
};
If the element isn't a type guard, then the resulting type should be unknown[]
, since we don't really know what the type should be.
Upvotes: 0