Skip to content

Commit

Permalink
feat(paths): parameter for AutoPath delimiter
Browse files Browse the repository at this point in the history
  • Loading branch information
David Blass committed Mar 4, 2021
1 parent 0836f6e commit 6d5bbdc
Show file tree
Hide file tree
Showing 2 changed files with 50 additions and 30 deletions.
79 changes: 49 additions & 30 deletions sources/Function/AutoPath.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,64 +12,80 @@ import {Split} from '../String/Split'
/**
* @ignore
*/
type Index = (number | string)
type Index = number | string;

/**
* @ignore
*/
type KeyToIndex<K extends Key, SP extends List<Index>> =
number extends K ? Head<SP> : K & Index
type KeyToIndex<K extends Key, SP extends List<Index>> = number extends K
? Head<SP>
: K & Index;

/**
* @ignore
*/
type MetaPath<O, SP extends List<Index> = [], P extends List<Index> = []> = {
[K in keyof O]:
| MetaPath<O[K] & {}, Tail<SP>, [...P, KeyToIndex<K, SP>]>
| Join<[...P, KeyToIndex<K, SP>], '.'>
}
type MetaPath<
O,
Delimiter extends string,
SP extends List<Index> = [],
P extends List<Index> = []
> = {
[K in keyof O]:
| MetaPath<O[K] & {}, Delimiter, Tail<SP>, [...P, KeyToIndex<K, SP>]>
| Join<[...P, KeyToIndex<K, SP>], Delimiter>;
};

/**
* @ignore
*/
type NextPath<OP> =
// the next paths after property `K` are on sub objects
// O[K] === K | {x: '${K}.x' | {y: '${K}.x.y' ...}}
// So we access O[K] then we only keep the next paths
// To do this, we can just exclude `string` out of it:
// O[K] === {x: '${K}.x' | {y: '${K}.x.y' ...}}
// To do this, we create a union of what we just got
// This will yield a union of paths and meta paths
// We exclude the next paths (meta) paths by excluding
// `object`. Then we are left with the direct next path
Select<UnionOf<Exclude<OP, string> & {}>, string>
// the next paths after property `K` are on sub objects
// O[K] === K | {x: '${K}.x' | {y: '${K}.x.y' ...}}
// So we access O[K] then we only keep the next paths
// To do this, we can just exclude `string` out of it:
// O[K] === {x: '${K}.x' | {y: '${K}.x.y' ...}}
// To do this, we create a union of what we just got
// This will yield a union of paths and meta paths
// We exclude the next paths (meta) paths by excluding
// `object`. Then we are left with the direct next path
Select<UnionOf<Exclude<OP, string> & {}>, string>;

/**
* @ignore
*/
type ExecPath<A, SP extends List<Index>> =
// We go in the `MetaPath` of `O` to get the prop at `SP`
// So we query what is going the `NextPath` at `O[...SP]`
NextPath<Path<MetaPath<A, SP>, SP>>
type ExecPath<A, SP extends List<Index>, Delimiter extends string> =
// We go in the `MetaPath` of `O` to get the prop at `SP`
// So we query what is going the `NextPath` at `O[...SP]`
NextPath<Path<MetaPath<A, Delimiter, SP>, SP>>;

/**
* @ignore
*/
type HintPath<A, P extends string, SP extends List<Index>, Exec extends string> =
[Exec] extends [never] // if has not found paths
? ExecPath<A, Pop<SP>> // display previous paths
: Exec | P // display current + next
type HintPath<
A,
P extends string,
SP extends List<Index>,
Exec extends string,
Delimiter extends string
> = [Exec] extends [never] // if has not found paths
? ExecPath<A, Pop<SP>, Delimiter> // display previous paths
: Exec | P; // display current + next

/**
* @ignore
*/
type _AutoPath<A, P extends string, SP extends List<Index> = Split<P, '.'>> =
HintPath<A, P, SP, ExecPath<A, SP>>
type _AutoPath<
A,
P extends string,
Delimiter extends string,
SP extends List<Index> = Split<P, Delimiter>
> = HintPath<A, P, SP, ExecPath<A, SP, Delimiter>, Delimiter>;

/**
* Auto-complete, validate, and query the string path of an object `O`
* @param O to work on
* @param P path of `O`
* @param Delimiter delimiter for path (default is ".")
*
* ```ts
* declare function get<O extends object, P extends string>(
Expand All @@ -92,5 +108,8 @@ type _AutoPath<A, P extends string, SP extends List<Index> = Split<P, '.'>> =
* const friendFriendNames = get(user, 'friends.40.friends.12.names')
* ```
*/
export type AutoPath<O extends any, P extends string> =
_AutoPath<O & {}, P>
export type AutoPath<
O extends any,
P extends string,
Delimiter extends string = '.'
> = _AutoPath<O & {}, P, Delimiter>;
1 change: 1 addition & 0 deletions tests/Function.ts
Original file line number Diff line number Diff line change
Expand Up @@ -120,6 +120,7 @@ checks([
check<F.AutoPath<O_PATHAUTO, 'b.'>, 'b.b' | 'b.a', Test.Pass>(),
check<F.AutoPath<O_PATHAUTO, 'b.b.0'>, 'b.b.0' | 'b.b.0.b' | 'b.b.0.a', Test.Pass>(),
check<F.AutoPath<O_PATHAUTO, 'b.b.0.a'>, 'b.b.0.a' | 'b.b.0.a.a', Test.Pass>(),
check<F.AutoPath<O_PATHAUTO, 'b/b/0/a', '/'>, 'b/b/0/a' | 'b/b/0/a/a', Test.Pass>(),
check<F.AutoPath<O_PATHAUTO, 'b.b.0.a'>, 'b.b.0.a' | 'b.b.x.a.a', Test.Fail>(),
check<F.AutoPath<O_PATHAUTO, 'b.b.0.a'>, 'b.b.0.a' | 'b.b.a.a', Test.Fail>(),
check<F.AutoPath<GlobalEventHandlersEventMap, 'cancel.isTrusted.'>, 'cancel.isTrusted.valueOf', Test.Pass>(),
Expand Down

0 comments on commit 6d5bbdc

Please sign in to comment.