82 lines
2.7 KiB
TypeScript
82 lines
2.7 KiB
TypeScript
type JoinDot<K extends string, P extends string> = `${K}${"" extends K ? "" : "."}${P}`;
|
|
|
|
type PathDot<T, Acc extends string = ""> = T extends object
|
|
? {
|
|
[K in keyof T]: K extends string ? JoinDot<Acc, K> | PathDot<T[K], JoinDot<Acc, K>> : never;
|
|
}[keyof T]
|
|
: Acc;
|
|
|
|
type PathHead<T extends unknown[]> = T extends [infer head]
|
|
? head
|
|
: // eslint-disable-next-line @typescript-eslint/no-unused-vars
|
|
T extends [infer head, ...infer rest]
|
|
? head
|
|
: "";
|
|
|
|
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
|
type PathRest<T extends unknown[]> = T extends [infer head, ...infer rest]
|
|
? rest extends []
|
|
? never
|
|
: rest
|
|
: never;
|
|
|
|
type PathLast<T extends unknown[]> = T["length"] extends 1 ? true : false;
|
|
|
|
type Recursive<T, Path extends unknown[]> = PathHead<Path> extends keyof T
|
|
? Omit<T, PathHead<Path>> & {
|
|
[P in PathHead<Path>]-?: PathLast<Path> extends true
|
|
? NonNullable<T[P]>
|
|
: Recursive<NonNullable<T[P]>, PathRest<Path>>;
|
|
}
|
|
: T;
|
|
|
|
type Split<Str, Acc extends string[] = []> = Str extends `${infer Head}.${infer Rest}`
|
|
? Split<Rest, [...Acc, Head]>
|
|
: Str extends `${infer Last}`
|
|
? [...Acc, Last]
|
|
: never;
|
|
|
|
type SelectiveNonNullable<T, P extends PathDot<T>> = Recursive<NonNullable<T>, Split<P>>;
|
|
|
|
export const isDefined = <T>(t: T): t is NonNullable<T> => t !== null && t !== undefined;
|
|
|
|
export const isUndefined = <T>(t: T | null | undefined): t is null | undefined => !isDefined(t);
|
|
|
|
export const isDefinedAndNotEmpty = (string: string | null | undefined): string is string =>
|
|
isDefined(string) && string.length > 0;
|
|
|
|
export const filterDefined = <T>(t: T[] | null | undefined): NonNullable<T>[] =>
|
|
isUndefined(t) ? [] : (t.filter((item) => isDefined(item)) as NonNullable<T>[]);
|
|
|
|
export const filterHasAttributes = <T, const P extends PathDot<T>>(
|
|
t: T[] | null | undefined,
|
|
paths: readonly P[]
|
|
): SelectiveNonNullable<T, (typeof paths)[number]>[] =>
|
|
isDefined(t)
|
|
? (t.filter((item) => hasAttributes(item, paths)) as unknown as SelectiveNonNullable<
|
|
T,
|
|
(typeof paths)[number]
|
|
>[])
|
|
: [];
|
|
|
|
const hasAttributes = <T>(item: T, paths: readonly PathDot<T>[]): boolean =>
|
|
isDefined(item) && paths.every((path) => hasAttribute(item, path));
|
|
|
|
const hasAttribute = <T>(item: T, path: string): boolean => {
|
|
if (isDefined(item)) {
|
|
const [head, ...rest] = path.split(".");
|
|
if (isDefined(head) && Object.keys(item).includes(head)) {
|
|
const attribute = head as keyof T;
|
|
if (isDefined(item[attribute])) {
|
|
if (rest.length > 0) {
|
|
return hasAttribute(item[attribute], rest.join("."));
|
|
}
|
|
return true;
|
|
}
|
|
}
|
|
}
|
|
return false;
|
|
};
|
|
|
|
|