Skip to content

Commit

Permalink
feat : make filteer method more type-safe
Browse files Browse the repository at this point in the history
  • Loading branch information
kakasoo committed Jan 31, 2024
1 parent 97e431c commit eaf0aa5
Show file tree
Hide file tree
Showing 3 changed files with 54 additions and 40 deletions.
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@kakasoo/proto-typescript",
"version": "1.27.0",
"version": "1.27.1",
"publishConfig": {
"access": "public"
},
Expand Down
75 changes: 40 additions & 35 deletions src/prototypes/array.prototype.ts
Original file line number Diff line number Diff line change
@@ -1,55 +1,60 @@
import { ArrayType, NumberType } from '../types';
import { Primitive, ReadonlyOrNot } from '../types/primitive.type';

function filter<Container extends ReadonlyOrNot<any[]>>(
conatiner: Container,
predicate: <Index extends number>(value: ArrayType.At<Container, Index>, index?: Index, array?: Container) => boolean,
): Partial<Container>;
function filter<Container extends ReadonlyOrNot<any[]>, FilterNull extends boolean, FilterUndefined extends boolean>(
type TypePredicate<Container extends ReadonlyOrNot<any[]>, Target> = {
<Index extends number, __Target extends Target = Target>(
value: ArrayType.At<Container, Index>,
index?: Index,
array?: Container,
): value is Target;
};

/**
* Filter `null` or `undefined` element of Array Container.
* @param container
* @param predicate filtering options
* @returns
*/
function filterNullish<
Container extends ReadonlyOrNot<any[]>,
FilterNull extends boolean,
FilterUndefined extends boolean,
>(
container: Container,
predicate: {
filterNull: FilterNull;
filterUndefined: FilterUndefined;
},
): ArrayType.Filter<Container, FilterNull, FilterUndefined>;
function filter<Container extends ReadonlyOrNot<any[]>, FilterNull extends boolean, FilterUndefined extends boolean>(
container: Container,
predicate:
| {
filterNull: FilterNull;
filterUndefined: FilterUndefined;
}
| (<Index extends number>(value: ArrayType.At<Container, Index>, index?: Index, array?: Container) => boolean),
): ArrayType.Filter<Container, FilterNull, FilterUndefined> | Partial<Container> {
return (
predicate instanceof Function
? container.filter(predicate as any)
: typeof predicate === 'object' &&
typeof predicate.filterNull === 'boolean' &&
typeof predicate.filterUndefined === 'boolean'
? container.filter((element) => {
if (predicate.filterNull === true) {
return element !== null;
}
): ArrayType.FilterNullish<Container, FilterNull, FilterUndefined> {
return container.filter((element) => {
if (predicate.filterNull === true) {
return element !== null;
}

if (predicate.filterUndefined === true) {
return element !== undefined;
}
if (predicate.filterUndefined === true) {
return element !== undefined;
}

return true;
})
: container
) as ArrayType.Filter<Container, FilterNull, FilterUndefined>;
return true;
}) as ArrayType.FilterNullish<Container, FilterNull, FilterUndefined>;
}

export const ArrayPrototype = {
/**
* Filter `null` or `undefined` element of Array Container.
* @param container
* @param options filtering options
* @param predicate
* @example
* ```ts
* const answer = ArrayPrototype.filter<[1, 2, 3, 4, 5], 2>([1, 2, 3, 4, 5] as const, (el: any): el is 2 => el === 2);
* ```
* @returns
*/
filter: filter,
filter<Container extends ReadonlyOrNot<any[]>, Target = any>(
container: Container,
predicate: TypePredicate<Container, Target>,
): ArrayType.Filter<Container, Target> {
return container.filter(predicate as any) as any;
},

/**
* It only returns the 0th index without subtracting the elements inside the actual container.
Expand Down
17 changes: 13 additions & 4 deletions src/types/array.type.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,15 @@ import { Equal, ObjectType } from './object.type';
import { Primitive, ReadonlyOrNot } from './primitive.type';

export namespace ArrayType {
/**
* @todo don't use `Equal` type and use `includes` of `union types` ( make `union incldues` type )
*/
export type Filter<T extends ReadonlyOrNot<any[]>, Target> = T extends [infer First, ...infer Rest]
? Equal<First, Target> extends true
? [First, ...Filter<Rest, Target>]
: Filter<Rest, Target>
: [];

type _FilterNull<FilterNull extends boolean, Target> = FilterNull extends true
? Equal<Target, null> extends true
? never
Expand All @@ -19,16 +28,16 @@ export namespace ArrayType {
/**
* type a = ArrayType.Filter<[1, 2, true, 3, undefined, null, 5], true, true> // [1, 2, 3, 5]
*/
export type Filter<
export type FilterNullish<
T extends ReadonlyOrNot<any[]>,
AllowNull extends boolean,
AllowUndefined extends boolean,
> = T extends [infer First, ...infer Rest]
? NeverType.IsNever<_FilterNull<AllowNull, First>> extends true
? Filter<Rest, AllowNull, AllowUndefined>
? FilterNullish<Rest, AllowNull, AllowUndefined>
: NeverType.IsNever<_FilterUndefined<AllowUndefined, First>> extends true
? Filter<Rest, AllowNull, AllowUndefined>
: [First, ...Filter<Rest, AllowNull, AllowUndefined>]
? FilterNullish<Rest, AllowNull, AllowUndefined>
: [First, ...FilterNullish<Rest, AllowNull, AllowUndefined>]
: [];

/**
Expand Down

0 comments on commit eaf0aa5

Please sign in to comment.