forked from ts-essentials/ts-essentials
-
Notifications
You must be signed in to change notification settings - Fork 0
/
index.ts
113 lines (101 loc) · 3.77 KB
/
index.ts
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
import { Builtin } from "../built-in";
import { IsAny } from "../is-any";
import { IsNever } from "../is-never";
import { CreateTypeOptions } from "../create-type-options";
import { ValueOf } from "../value-of";
type Pathable = string | number;
// Prevent inference of non-recursive type methods, e.g. Promise.then, Map.get, etc
type NonRecursiveType = Builtin | Promise<unknown> | ReadonlyMap<unknown, unknown> | ReadonlySet<unknown>;
type DefaultRecursivePathsOptions = {
depth: [];
};
/**
* @param depth This option counts the number of recursive calls in
* `RecursivePathsOptions['depth']['length']`. Used in combination with
* `PathsOptions['depth']`
*/
type RecursivePathsOptions = {
depth: any[];
};
/**
* @param depth By default, the depth option is set to 7. It should cover the
* majority of use cases. If by any chance it doesn't fit you, feel free to
* increase the value. However, this may increase the chance of getting
* `Type instantiation is excessively deep and possibly infinite` error.
*
* @param anyArrayIndexAccessor By default there is no wildcard access to
* array indices - usage must be intentionally configured.
*/
type DefaultPathsOptions = {
depth: 7;
anyArrayIndexAccessor: `${number}`;
};
/**
* @param depth This option restricts the depth of the paths lookup and removes `Type
* instantiation is excessively deep and possibly infinite` errors for
* potentially infinite types.
*
* @param anyArrayIndexAccessor This wildcard will satisfy any array index if defined.
*/
type PathsOptions = {
depth: number;
anyArrayIndexAccessor: string;
};
type Append<Tuple extends any[]> = [...Tuple, 0];
type RecursivePaths<
Type,
UserOptions extends Required<PathsOptions>,
CallOptions extends RecursivePathsOptions,
> = IsNever<keyof Type> extends true
? never
: // `NonNullable` removes `undefined` when partial properties exist in object
NonNullable<
ValueOf<{
[Key in keyof Type]: Key extends Pathable
?
| `${AnyArrayIndexAccessorOrKey<Key, UserOptions>}`
| (CallOptions["depth"]["length"] extends UserOptions["depth"]
? // Stop at the configured depth
never
: Type[Key] extends infer Value
? Value extends Value
? // Avoid calling `UnsafePaths` to keep `CallOptions` locally
HasParsablePath<Value> extends true
? RecursivePaths<
Value,
UserOptions,
{
depth: Append<CallOptions["depth"]>;
}
> extends infer Rest
? IsNever<Rest> extends true
? never
: Rest extends Pathable
? `${AnyArrayIndexAccessorOrKey<Key, UserOptions>}.${Rest}`
: never
: never
: never
: never
: never)
: never;
}>
>;
type HasParsablePath<Type> = Type extends NonRecursiveType
? false
: IsAny<Type> extends true
? false
: Type extends object
? true
: false;
type UnsafePaths<Type, Options extends Required<PathsOptions>> = Type extends Type
? HasParsablePath<Type> extends true
? RecursivePaths<Type, Options, DefaultRecursivePathsOptions>
: never
: never;
type AnyArrayIndexAccessorOrKey<Key extends Pathable, UserOptions extends Required<PathsOptions>> = Key extends number
? Key | UserOptions["anyArrayIndexAccessor"]
: Key;
export type Paths<Type, OverridePathOptions extends Partial<PathsOptions> = {}> = UnsafePaths<
Type,
CreateTypeOptions<PathsOptions, OverridePathOptions, DefaultPathsOptions>
>;