@visulima/object 1.0.8 → 1.0.10

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.d.cts CHANGED
@@ -19,6 +19,68 @@ declare global {
19
19
  }
20
20
  }
21
21
 
22
+ /**
23
+ Convert a union type to an intersection type using [distributive conditional types](https://www.typescriptlang.org/docs/handbook/release-notes/typescript-2-8.html#distributive-conditional-types).
24
+
25
+ Inspired by [this Stack Overflow answer](https://stackoverflow.com/a/50375286/2172153).
26
+
27
+ @example
28
+ ```
29
+ import type {UnionToIntersection} from 'type-fest';
30
+
31
+ type Union = {the(): void} | {great(arg: string): void} | {escape: boolean};
32
+
33
+ type Intersection = UnionToIntersection<Union>;
34
+ //=> {the(): void; great(arg: string): void; escape: boolean};
35
+ ```
36
+
37
+ A more applicable example which could make its way into your library code follows.
38
+
39
+ @example
40
+ ```
41
+ import type {UnionToIntersection} from 'type-fest';
42
+
43
+ class CommandOne {
44
+ commands: {
45
+ a1: () => undefined,
46
+ b1: () => undefined,
47
+ }
48
+ }
49
+
50
+ class CommandTwo {
51
+ commands: {
52
+ a2: (argA: string) => undefined,
53
+ b2: (argB: string) => undefined,
54
+ }
55
+ }
56
+
57
+ const union = [new CommandOne(), new CommandTwo()].map(instance => instance.commands);
58
+ type Union = typeof union;
59
+ //=> {a1(): void; b1(): void} | {a2(argA: string): void; b2(argB: string): void}
60
+
61
+ type Intersection = UnionToIntersection<Union>;
62
+ //=> {a1(): void; b1(): void; a2(argA: string): void; b2(argB: string): void}
63
+ ```
64
+
65
+ @category Type
66
+ */
67
+ type UnionToIntersection<Union> = (
68
+ // `extends unknown` is always going to be the case and is used to convert the
69
+ // `Union` into a [distributive conditional
70
+ // type](https://www.typescriptlang.org/docs/handbook/release-notes/typescript-2-8.html#distributive-conditional-types).
71
+ Union extends unknown
72
+ // The union type is used as the only argument to a function since the union
73
+ // of function arguments is an intersection.
74
+ ? (distributedUnion: Union) => void
75
+ // This won't happen.
76
+ : never
77
+ // Infer the `Intersection` type since TypeScript represents the positional
78
+ // arguments of unions of functions as an intersection of the union.
79
+ ) extends ((mergedIntersection: infer Intersection) => void)
80
+ // The `& Union` is to allow indexing by the resulting type
81
+ ? Intersection & Union
82
+ : never;
83
+
22
84
  declare const emptyObjectSymbol: unique symbol;
23
85
 
24
86
  /**
@@ -390,6 +452,71 @@ type ShouldBeTrue = IsNegative<-1>;
390
452
  */
391
453
  type IsNegative<T extends Numeric> = T extends Negative<T> ? true : false;
392
454
 
455
+ /**
456
+ Returns a boolean for whether the given type is a `string` [literal type](https://www.typescriptlang.org/docs/handbook/2/everyday-types.html#literal-types).
457
+
458
+ Useful for:
459
+ - providing strongly-typed string manipulation functions
460
+ - constraining strings to be a string literal
461
+ - type utilities, such as when constructing parsers and ASTs
462
+
463
+ The implementation of this type is inspired by the trick mentioned in this [StackOverflow answer](https://stackoverflow.com/a/68261113/420747).
464
+
465
+ @example
466
+ ```
467
+ import type {IsStringLiteral} from 'type-fest';
468
+
469
+ type CapitalizedString<T extends string> = IsStringLiteral<T> extends true ? Capitalize<T> : string;
470
+
471
+ // https://github.com/yankeeinlondon/native-dash/blob/master/src/capitalize.ts
472
+ function capitalize<T extends Readonly<string>>(input: T): CapitalizedString<T> {
473
+ return (input.slice(0, 1).toUpperCase() + input.slice(1)) as CapitalizedString<T>;
474
+ }
475
+
476
+ const output = capitalize('hello, world!');
477
+ //=> 'Hello, world!'
478
+ ```
479
+
480
+ @example
481
+ ```
482
+ // String types with infinite set of possible values return `false`.
483
+
484
+ import type {IsStringLiteral} from 'type-fest';
485
+
486
+ type AllUppercaseStrings = IsStringLiteral<Uppercase<string>>;
487
+ //=> false
488
+
489
+ type StringsStartingWithOn = IsStringLiteral<`on${string}`>;
490
+ //=> false
491
+
492
+ // This behaviour is particularly useful in string manipulation utilities, as infinite string types often require separate handling.
493
+
494
+ type Length<S extends string, Counter extends never[] = []> =
495
+ IsStringLiteral<S> extends false
496
+ ? number // return `number` for infinite string types
497
+ : S extends `${string}${infer Tail}`
498
+ ? Length<Tail, [...Counter, never]>
499
+ : Counter['length'];
500
+
501
+ type L1 = Length<Lowercase<string>>;
502
+ //=> number
503
+
504
+ type L2 = Length<`${number}`>;
505
+ //=> number
506
+ ```
507
+
508
+ @category Type Guard
509
+ @category Utilities
510
+ */
511
+ type IsStringLiteral<T> = IfNever<T, false,
512
+ // If `T` is an infinite string type (e.g., `on${string}`), `Record<T, never>` produces an index signature,
513
+ // and since `{}` extends index signatures, the result becomes `false`.
514
+ T extends string
515
+ ? {} extends Record<T, never>
516
+ ? false
517
+ : true
518
+ : false>;
519
+
393
520
  /**
394
521
  Returns a boolean for whether two given types are both true.
395
522
 
@@ -526,32 +653,7 @@ type LessThan<A extends number, B extends number> = number extends A | B
526
653
  ? never
527
654
  : GreaterThanOrEqual<A, B> extends true ? false : true;
528
655
 
529
- /**
530
- Infer the length of the given tuple `<T>`.
531
-
532
- Returns `never` if the given type is an non-fixed-length array like `Array<string>`.
533
-
534
- @example
535
- ```
536
- type Tuple = TupleLength<[string, number, boolean]>;
537
- //=> 3
538
-
539
- type Array = TupleLength<string[]>;
540
- //=> never
541
-
542
- // Supports union types.
543
- type Union = TupleLength<[] | [1, 2, 3] | Array<number>>;
544
- //=> 1 | 3
545
- ```
546
- */
547
- type TupleLength<T extends UnknownArray> =
548
- // `extends unknown` is used to convert `T` (if `T` is a union type) to
549
- // a [distributive conditionaltype](https://www.typescriptlang.org/docs/handbook/release-notes/typescript-2-8.html#distributive-conditional-types))
550
- T extends unknown
551
- ? number extends T['length']
552
- ? never // Return never if the given type is an non-flexed-length array like `Array<string>`
553
- : T['length']
554
- : never; // Should never happen
656
+ // Should never happen
555
657
 
556
658
  /**
557
659
  Create a tuple type of the given length `<L>` and fill it with the given type `<Fill>`.
@@ -560,55 +662,11 @@ If `<Fill>` is not provided, it will default to `unknown`.
560
662
 
561
663
  @link https://itnext.io/implementing-arithmetic-within-typescripts-type-system-a1ef140a6f6f
562
664
  */
563
- type BuildTuple<L extends number, Fill = unknown, T extends readonly unknown[] = []> = T['length'] extends L
564
- ? T
565
- : BuildTuple<L, Fill, [...T, Fill]>;
566
-
567
- /**
568
- Returns the maximum value from a tuple of integers.
569
-
570
- Note:
571
- - Float numbers are not supported.
572
-
573
- @example
574
- ```
575
- ArrayMax<[1, 2, 5, 3]>;
576
- //=> 5
577
-
578
- ArrayMax<[1, 2, 5, 3, 99, -1]>;
579
- //=> 99
580
- ```
581
- */
582
- type TupleMax<A extends number[], Result extends number = NegativeInfinity> = number extends A[number]
583
- ? never :
584
- A extends [infer F extends number, ...infer R extends number[]]
585
- ? GreaterThan<F, Result> extends true
586
- ? TupleMax<R, F>
587
- : TupleMax<R, Result>
588
- : Result;
589
-
590
- /**
591
- Returns the minimum value from a tuple of integers.
592
-
593
- Note:
594
- - Float numbers are not supported.
595
-
596
- @example
597
- ```
598
- ArrayMin<[1, 2, 5, 3]>;
599
- //=> 1
600
-
601
- ArrayMin<[1, 2, 5, 3, -5]>;
602
- //=> -5
603
- ```
604
- */
605
- type TupleMin<A extends number[], Result extends number = PositiveInfinity> = number extends A[number]
606
- ? never
607
- : A extends [infer F extends number, ...infer R extends number[]]
608
- ? LessThan<F, Result> extends true
609
- ? TupleMin<R, F>
610
- : TupleMin<R, Result>
611
- : Result;
665
+ type BuildTuple<L extends number, Fill = unknown, T extends readonly unknown[] = []> = number extends L
666
+ ? Fill[]
667
+ : L extends T['length']
668
+ ? T
669
+ : BuildTuple<L, Fill, [...T, Fill]>;
612
670
 
613
671
  /**
614
672
  Return a string representation of the given string or number.
@@ -840,48 +898,30 @@ type IsNumberLike<N> =
840
898
  : false;
841
899
 
842
900
  /**
843
- Returns the minimum number in the given union of numbers.
844
-
845
- Note: Just supports numbers from 0 to 999.
901
+ Returns the number with reversed sign.
846
902
 
847
903
  @example
848
904
  ```
849
- type A = UnionMin<3 | 1 | 2>;
905
+ ReverseSign<-1>;
850
906
  //=> 1
851
- ```
852
- */
853
- type UnionMin<N extends number> = InternalUnionMin<N>;
854
-
855
- /**
856
- The actual implementation of `UnionMin`. It's private because it has some arguments that don't need to be exposed.
857
- */
858
- type InternalUnionMin<N extends number, T extends UnknownArray = []> =
859
- T['length'] extends N
860
- ? T['length']
861
- : InternalUnionMin<N, [...T, unknown]>;
862
907
 
863
- /**
864
- Returns the maximum number in the given union of numbers.
908
+ ReverseSign<1>;
909
+ //=> -1
865
910
 
866
- Note: Just supports numbers from 0 to 999.
911
+ ReverseSign<NegativeInfinity>
912
+ //=> PositiveInfinity
867
913
 
868
- @example
869
- ```
870
- type A = UnionMax<1 | 3 | 2>;
871
- //=> 3
914
+ ReverseSign<PositiveInfinity>
915
+ //=> NegativeInfinity
872
916
  ```
873
917
  */
874
- type UnionMax<N extends number> = InternalUnionMax<N>;
875
-
876
- /**
877
- The actual implementation of `UnionMax`. It's private because it has some arguments that don't need to be exposed.
878
- */
879
- type InternalUnionMax<N extends number, T extends UnknownArray = []> =
880
- IsNever<N> extends true
881
- ? T['length']
882
- : T['length'] extends N
883
- ? InternalUnionMax<Exclude<N, T['length']>, T>
884
- : InternalUnionMax<N, [...T, unknown]>;
918
+ type ReverseSign<N extends number> =
919
+ // Handle edge cases
920
+ N extends 0 ? 0 : N extends PositiveInfinity ? NegativeInfinity : N extends NegativeInfinity ? PositiveInfinity :
921
+ // Handle negative numbers
922
+ `${N}` extends `-${infer P extends number}` ? P
923
+ // Handle positive numbers
924
+ : `-${N}` extends `${infer R extends number}` ? R : never;
885
925
 
886
926
  /**
887
927
  Matches any primitive, `void`, `Date`, or `RegExp` value.
@@ -893,6 +933,24 @@ Matches non-recursive types.
893
933
  */
894
934
  type NonRecursiveType = BuiltIns | Function | (new (...arguments_: any[]) => unknown);
895
935
 
936
+ /**
937
+ Returns a boolean for whether A is false.
938
+
939
+ @example
940
+ ```
941
+ Not<true>;
942
+ //=> false
943
+
944
+ Not<false>;
945
+ //=> true
946
+ ```
947
+ */
948
+ type Not<A extends boolean> = A extends true
949
+ ? false
950
+ : A extends false
951
+ ? true
952
+ : never;
953
+
896
954
  /**
897
955
  Create an object type with the given key `<Key>` and value `<Value>`.
898
956
 
@@ -1046,76 +1104,11 @@ type SimplifyDeep<Type, ExcludeType = never> =
1046
1104
  object
1047
1105
  >;
1048
1106
 
1049
- /**
1050
- Returns the sum of two numbers.
1051
-
1052
- Note:
1053
- - A or B can only support `-999` ~ `999`.
1054
- - A and B can only be small integers, less than 1000.
1055
- - If the result is negative, you can only get `number`.
1056
-
1057
- @example
1058
- ```
1059
- import type {Sum} from 'type-fest';
1060
-
1061
- Sum<111, 222>;
1062
- //=> 333
1063
-
1064
- Sum<-111, 222>;
1065
- //=> 111
1066
-
1067
- Sum<111, -222>;
1068
- //=> number
1069
-
1070
- Sum<PositiveInfinity, -9999>;
1071
- //=> PositiveInfinity
1072
-
1073
- Sum<PositiveInfinity, NegativeInfinity>;
1074
- //=> number
1075
- ```
1076
-
1077
- @category Numeric
1078
- */
1079
- // TODO: Support big integer and negative number.
1080
- type Sum<A extends number, B extends number> = number extends A | B
1081
- ? number
1082
- : [
1083
- IsEqual<A, PositiveInfinity>, IsEqual<A, NegativeInfinity>,
1084
- IsEqual<B, PositiveInfinity>, IsEqual<B, NegativeInfinity>,
1085
- ] extends infer R extends [boolean, boolean, boolean, boolean]
1086
- ? Or<
1087
- And<IsEqual<R[0], true>, IsEqual<R[3], false>>,
1088
- And<IsEqual<R[2], true>, IsEqual<R[1], false>>
1089
- > extends true
1090
- ? PositiveInfinity
1091
- : Or<
1092
- And<IsEqual<R[1], true>, IsEqual<R[2], false>>,
1093
- And<IsEqual<R[3], true>, IsEqual<R[0], false>>
1094
- > extends true
1095
- ? NegativeInfinity
1096
- : true extends R[number]
1097
- ? number
1098
- : ([IsNegative<A>, IsNegative<B>] extends infer R
1099
- ? [false, false] extends R
1100
- ? [...BuildTuple<A>, ...BuildTuple<B>]['length']
1101
- : [true, true] extends R
1102
- ? number
1103
- : TupleMax<[NumberAbsolute<A>, NumberAbsolute<B>]> extends infer Max_
1104
- ? TupleMin<[NumberAbsolute<A>, NumberAbsolute<B>]> extends infer Min_ extends number
1105
- ? Max_ extends A | B
1106
- ? Subtract<Max_, Min_>
1107
- : number
1108
- : never
1109
- : never
1110
- : never) & number
1111
- : never;
1112
-
1113
1107
  /**
1114
1108
  Returns the difference between two numbers.
1115
1109
 
1116
1110
  Note:
1117
1111
  - A or B can only support `-999` ~ `999`.
1118
- - If the result is negative, you can only get `number`.
1119
1112
 
1120
1113
  @example
1121
1114
  ```
@@ -1128,7 +1121,10 @@ Subtract<111, -222>;
1128
1121
  //=> 333
1129
1122
 
1130
1123
  Subtract<-111, 222>;
1131
- //=> number
1124
+ //=> -333
1125
+
1126
+ Subtract<18, 96>;
1127
+ //=> -78
1132
1128
 
1133
1129
  Subtract<PositiveInfinity, 9999>;
1134
1130
  //=> PositiveInfinity
@@ -1139,38 +1135,53 @@ Subtract<PositiveInfinity, PositiveInfinity>;
1139
1135
 
1140
1136
  @category Numeric
1141
1137
  */
1142
- // TODO: Support big integer and negative number.
1143
- type Subtract<A extends number, B extends number> = number extends A | B
1144
- ? number
1145
- : [
1146
- IsEqual<A, PositiveInfinity>, IsEqual<A, NegativeInfinity>,
1147
- IsEqual<B, PositiveInfinity>, IsEqual<B, NegativeInfinity>,
1148
- ] extends infer R extends [boolean, boolean, boolean, boolean]
1149
- ? Or<
1150
- And<IsEqual<R[0], true>, IsEqual<R[2], false>>,
1151
- And<IsEqual<R[3], true>, IsEqual<R[1], false>>
1152
- > extends true
1153
- ? PositiveInfinity
1154
- : Or<
1155
- And<IsEqual<R[1], true>, IsEqual<R[3], false>>,
1156
- And<IsEqual<R[2], true>, IsEqual<R[0], false>>
1157
- > extends true
1158
- ? NegativeInfinity
1159
- : true extends R[number]
1160
- ? number
1161
- : [IsNegative<A>, IsNegative<B>] extends infer R
1162
- ? [false, false] extends R
1163
- ? BuildTuple<A> extends infer R
1164
- ? R extends [...BuildTuple<B>, ...infer R]
1165
- ? R['length']
1166
- : number
1167
- : never
1168
- : LessThan<A, B> extends true
1169
- ? number
1170
- : [false, true] extends R
1171
- ? Sum<A, NumberAbsolute<B>>
1172
- : Subtract<NumberAbsolute<B>, NumberAbsolute<A>>
1173
- : never
1138
+ // TODO: Support big integer.
1139
+ type Subtract<A extends number, B extends number> =
1140
+ // Handle cases when A or B is the actual "number" type
1141
+ number extends A | B ? number
1142
+ // Handle cases when A and B are both +/- infinity
1143
+ : A extends B & (PositiveInfinity | NegativeInfinity) ? number
1144
+ // Handle cases when A is - infinity or B is + infinity
1145
+ : A extends NegativeInfinity ? NegativeInfinity : B extends PositiveInfinity ? NegativeInfinity
1146
+ // Handle cases when A is + infinity or B is - infinity
1147
+ : A extends PositiveInfinity ? PositiveInfinity : B extends NegativeInfinity ? PositiveInfinity
1148
+ // Handle case when numbers are equal to each other
1149
+ : A extends B ? 0
1150
+ // Handle cases when A or B is 0
1151
+ : A extends 0 ? ReverseSign<B> : B extends 0 ? A
1152
+ // Handle remaining regular cases
1153
+ : SubtractPostChecks<A, B>;
1154
+
1155
+ /**
1156
+ Subtracts two numbers A and B, such that they are not equal and neither of them are 0, +/- infinity or the `number` type
1157
+ */
1158
+ type SubtractPostChecks<A extends number, B extends number, AreNegative = [IsNegative<A>, IsNegative<B>]> =
1159
+ AreNegative extends [false, false]
1160
+ ? SubtractPositives<A, B>
1161
+ : AreNegative extends [true, true]
1162
+ // When both numbers are negative we subtract the absolute values and then reverse the sign
1163
+ ? ReverseSign<SubtractPositives<NumberAbsolute<A>, NumberAbsolute<B>>>
1164
+ // When the signs are different we can add the absolute values and then reverse the sign if A < B
1165
+ : [...BuildTuple<NumberAbsolute<A>>, ...BuildTuple<NumberAbsolute<B>>] extends infer R extends unknown[]
1166
+ ? LessThan<A, B> extends true ? ReverseSign<R['length']> : R['length']
1167
+ : never;
1168
+
1169
+ /**
1170
+ Subtracts two positive numbers.
1171
+ */
1172
+ type SubtractPositives<A extends number, B extends number> =
1173
+ LessThan<A, B> extends true
1174
+ // When A < B we can reverse the result of B - A
1175
+ ? ReverseSign<SubtractIfAGreaterThanB<B, A>>
1176
+ : SubtractIfAGreaterThanB<A, B>;
1177
+
1178
+ /**
1179
+ Subtracts two positive numbers A and B such that A > B.
1180
+ */
1181
+ type SubtractIfAGreaterThanB<A extends number, B extends number> =
1182
+ // This is where we always want to end up and do the actual subtraction
1183
+ BuildTuple<A> extends [...BuildTuple<B>, ...infer R]
1184
+ ? R['length']
1174
1185
  : never;
1175
1186
 
1176
1187
  /**
@@ -1218,11 +1229,89 @@ type PathsOptions = {
1218
1229
  ```
1219
1230
  */
1220
1231
  bracketNotation?: boolean;
1232
+
1233
+ /**
1234
+ Only include leaf paths in the output.
1235
+
1236
+ @default false
1237
+
1238
+ @example
1239
+ ```
1240
+ type Post = {
1241
+ id: number;
1242
+ author: {
1243
+ id: number;
1244
+ name: {
1245
+ first: string;
1246
+ last: string;
1247
+ };
1248
+ };
1249
+ };
1250
+
1251
+ type AllPaths = Paths<Post, {leavesOnly: false}>;
1252
+ //=> 'id' | 'author' | 'author.id' | 'author.name' | 'author.name.first' | 'author.name.last'
1253
+
1254
+ type LeafPaths = Paths<Post, {leavesOnly: true}>;
1255
+ //=> 'id' | 'author.id' | 'author.name.first' | 'author.name.last'
1256
+ ```
1257
+
1258
+ @example
1259
+ ```
1260
+ type ArrayExample = {
1261
+ array: Array<{foo: string}>;
1262
+ tuple: [string, {bar: string}];
1263
+ };
1264
+
1265
+ type AllPaths = Paths<ArrayExample, {leavesOnly: false}>;
1266
+ //=> 'array' | `array.${number}` | `array.${number}.foo` | 'tuple' | 'tuple.0' | 'tuple.1' | 'tuple.1.bar'
1267
+
1268
+ type LeafPaths = Paths<ArrayExample, {leavesOnly: true}>;
1269
+ //=> `array.${number}.foo` | 'tuple.0' | 'tuple.1.bar'
1270
+ ```
1271
+ */
1272
+ leavesOnly?: boolean;
1273
+
1274
+ /**
1275
+ Only include paths at the specified depth. By default all paths up to {@link PathsOptions.maxRecursionDepth | `maxRecursionDepth`} are included.
1276
+
1277
+ Note: Depth starts at `0` for root properties.
1278
+
1279
+ @default number
1280
+
1281
+ @example
1282
+ ```
1283
+ type Post = {
1284
+ id: number;
1285
+ author: {
1286
+ id: number;
1287
+ name: {
1288
+ first: string;
1289
+ last: string;
1290
+ };
1291
+ };
1292
+ };
1293
+
1294
+ type DepthZero = Paths<Post, {depth: 0}>;
1295
+ //=> 'id' | 'author'
1296
+
1297
+ type DepthOne = Paths<Post, {depth: 1}>;
1298
+ //=> 'author.id' | 'author.name'
1299
+
1300
+ type DepthTwo = Paths<Post, {depth: 2}>;
1301
+ //=> 'author.name.first' | 'author.name.last'
1302
+
1303
+ type LeavesAtDepthOne = Paths<Post, {leavesOnly: true; depth: 1}>;
1304
+ //=> 'author.id'
1305
+ ```
1306
+ */
1307
+ depth?: number;
1221
1308
  };
1222
1309
 
1223
1310
  type DefaultPathsOptions = {
1224
1311
  maxRecursionDepth: 10;
1225
1312
  bracketNotation: false;
1313
+ leavesOnly: false;
1314
+ depth: number;
1226
1315
  };
1227
1316
 
1228
1317
  /**
@@ -1271,6 +1360,10 @@ type Paths<T, Options extends PathsOptions = {}> = _Paths<T, {
1271
1360
  maxRecursionDepth: Options['maxRecursionDepth'] extends number ? Options['maxRecursionDepth'] : DefaultPathsOptions['maxRecursionDepth'];
1272
1361
  // Set default bracketNotation to false
1273
1362
  bracketNotation: Options['bracketNotation'] extends boolean ? Options['bracketNotation'] : DefaultPathsOptions['bracketNotation'];
1363
+ // Set default leavesOnly to false
1364
+ leavesOnly: Options['leavesOnly'] extends boolean ? Options['leavesOnly'] : DefaultPathsOptions['leavesOnly'];
1365
+ // Set default depth to number
1366
+ depth: Options['depth'] extends number ? Options['depth'] : DefaultPathsOptions['depth'];
1274
1367
  }>;
1275
1368
 
1276
1369
  type _Paths<T, Options extends Required<PathsOptions>> =
@@ -1310,11 +1403,30 @@ type InternalPaths<T, Options extends Required<PathsOptions>> =
1310
1403
  ) extends infer TranformedKey extends string | number ?
1311
1404
  // 1. If style is 'a[0].b' and 'Key' is a numberlike value like 3 or '3', transform 'Key' to `[${Key}]`, else to `${Key}` | Key
1312
1405
  // 2. If style is 'a.0.b', transform 'Key' to `${Key}` | Key
1313
- | TranformedKey
1406
+ | ((Options['leavesOnly'] extends true
1407
+ ? MaxDepth extends 0
1408
+ ? TranformedKey
1409
+ : T[Key] extends EmptyObject | readonly [] | NonRecursiveType | ReadonlyMap<unknown, unknown> | ReadonlySet<unknown>
1410
+ ? TranformedKey
1411
+ : never
1412
+ : TranformedKey
1413
+ ) extends infer _TransformedKey
1414
+ // If `depth` is provided, the condition becomes truthy only when it reaches `0`.
1415
+ // Otherwise, since `depth` defaults to `number`, the condition is always truthy, returning paths at all depths.
1416
+ ? 0 extends Options['depth']
1417
+ ? _TransformedKey
1418
+ : never
1419
+ : never)
1314
1420
  | (
1315
1421
  // Recursively generate paths for the current key
1316
1422
  GreaterThan<MaxDepth, 0> extends true // Limit the depth to prevent infinite recursion
1317
- ? _Paths<T[Key], {bracketNotation: Options['bracketNotation']; maxRecursionDepth: Subtract<MaxDepth, 1>}> extends infer SubPath
1423
+ ? _Paths<T[Key],
1424
+ {
1425
+ bracketNotation: Options['bracketNotation'];
1426
+ maxRecursionDepth: Subtract<MaxDepth, 1>;
1427
+ leavesOnly: Options['leavesOnly'];
1428
+ depth: Subtract<Options['depth'], 1>;
1429
+ }> extends infer SubPath
1318
1430
  ? SubPath extends string | number
1319
1431
  ? (
1320
1432
  Options['bracketNotation'] extends true
@@ -1337,68 +1449,6 @@ type InternalPaths<T, Options extends Required<PathsOptions>> =
1337
1449
  : never
1338
1450
  : never;
1339
1451
 
1340
- /**
1341
- Convert a union type to an intersection type using [distributive conditional types](https://www.typescriptlang.org/docs/handbook/release-notes/typescript-2-8.html#distributive-conditional-types).
1342
-
1343
- Inspired by [this Stack Overflow answer](https://stackoverflow.com/a/50375286/2172153).
1344
-
1345
- @example
1346
- ```
1347
- import type {UnionToIntersection} from 'type-fest';
1348
-
1349
- type Union = {the(): void} | {great(arg: string): void} | {escape: boolean};
1350
-
1351
- type Intersection = UnionToIntersection<Union>;
1352
- //=> {the(): void; great(arg: string): void; escape: boolean};
1353
- ```
1354
-
1355
- A more applicable example which could make its way into your library code follows.
1356
-
1357
- @example
1358
- ```
1359
- import type {UnionToIntersection} from 'type-fest';
1360
-
1361
- class CommandOne {
1362
- commands: {
1363
- a1: () => undefined,
1364
- b1: () => undefined,
1365
- }
1366
- }
1367
-
1368
- class CommandTwo {
1369
- commands: {
1370
- a2: (argA: string) => undefined,
1371
- b2: (argB: string) => undefined,
1372
- }
1373
- }
1374
-
1375
- const union = [new CommandOne(), new CommandTwo()].map(instance => instance.commands);
1376
- type Union = typeof union;
1377
- //=> {a1(): void; b1(): void} | {a2(argA: string): void; b2(argB: string): void}
1378
-
1379
- type Intersection = UnionToIntersection<Union>;
1380
- //=> {a1(): void; b1(): void; a2(argA: string): void; b2(argB: string): void}
1381
- ```
1382
-
1383
- @category Type
1384
- */
1385
- type UnionToIntersection<Union> = (
1386
- // `extends unknown` is always going to be the case and is used to convert the
1387
- // `Union` into a [distributive conditional
1388
- // type](https://www.typescriptlang.org/docs/handbook/release-notes/typescript-2-8.html#distributive-conditional-types).
1389
- Union extends unknown
1390
- // The union type is used as the only argument to a function since the union
1391
- // of function arguments is an intersection.
1392
- ? (distributedUnion: Union) => void
1393
- // This won't happen.
1394
- : never
1395
- // Infer the `Intersection` type since TypeScript represents the positional
1396
- // arguments of unions of functions as an intersection of the union.
1397
- ) extends ((mergedIntersection: infer Intersection) => void)
1398
- // The `& Union` is to allow indexing by the resulting type
1399
- ? Intersection & Union
1400
- : never;
1401
-
1402
1452
  /**
1403
1453
  Pick properties from a deeply-nested object.
1404
1454
 
@@ -1560,7 +1610,9 @@ The implementation of `SplitArrayByIndex` for variable length arrays.
1560
1610
  type SplitVariableArrayByIndex<T extends UnknownArray,
1561
1611
  SplitIndex extends number,
1562
1612
  T1 = Subtract<SplitIndex, StaticPartOfArray<T>['length']>,
1563
- T2 = T1 extends number ? BuildTuple<T1, VariablePartOfArray<T>[number]> : [],
1613
+ T2 = T1 extends number
1614
+ ? BuildTuple<GreaterThanOrEqual<T1, 0> extends true ? T1 : number, VariablePartOfArray<T>[number]>
1615
+ : [],
1564
1616
  > =
1565
1617
  SplitIndex extends 0
1566
1618
  ? [[], T]
@@ -1672,173 +1724,58 @@ type LiteralUnion<
1672
1724
  > = LiteralType | (BaseType & Record<never, never>);
1673
1725
 
1674
1726
  /**
1675
- SharedUnionFieldsDeep options.
1727
+ Returns the last element of a union type.
1676
1728
 
1677
- @see {@link SharedUnionFieldsDeep}
1729
+ @example
1730
+ ```
1731
+ type Last = LastOfUnion<1 | 2 | 3>;
1732
+ //=> 3
1733
+ ```
1678
1734
  */
1679
- type SharedUnionFieldsDeepOptions = {
1680
- /**
1681
- When set to true, this option impacts each element within arrays or tuples. If all union values are arrays or tuples, it constructs an array of the shortest possible length, ensuring every element exists in the union array.
1682
-
1683
- @default false
1684
- */
1685
- recurseIntoArrays?: boolean;
1686
- };
1735
+ type LastOfUnion<T> =
1736
+ UnionToIntersection<T extends any ? () => T : never> extends () => (infer R)
1737
+ ? R
1738
+ : never;
1687
1739
 
1688
1740
  /**
1689
- Create a type with shared fields from a union of object types, deeply traversing nested structures.
1741
+ Convert a union type into an unordered tuple type of its elements.
1690
1742
 
1691
- Use the {@link SharedUnionFieldsDeepOptions `Options`} to specify the behavior for arrays.
1743
+ "Unordered" means the elements of the tuple are not guaranteed to be in the same order as in the union type. The arrangement can appear random and may change at any time.
1692
1744
 
1693
- Use-cases:
1694
- - You want a safe object type where each key exists in the union object.
1695
- - You want to focus on the common fields of the union type and don't want to have to care about the other fields.
1745
+ This can be useful when you have objects with a finite set of keys and want a type defining only the allowed keys, but do not want to repeat yourself.
1696
1746
 
1697
1747
  @example
1698
1748
  ```
1699
- import type {SharedUnionFieldsDeep} from 'type-fest';
1749
+ import type {UnionToTuple} from 'type-fest';
1700
1750
 
1701
- type Cat = {
1702
- info: {
1703
- name: string;
1704
- type: 'cat';
1705
- catType: string;
1706
- };
1707
- };
1708
-
1709
- type Dog = {
1710
- info: {
1711
- name: string;
1712
- type: 'dog';
1713
- dogType: string;
1714
- };
1715
- };
1716
-
1717
- function displayPetInfo(petInfo: (Cat | Dog)['info']) {
1718
- // typeof petInfo =>
1719
- // {
1720
- // name: string;
1721
- // type: 'cat';
1722
- // catType: string; // Needn't care about this field, because it's not a common pet info field.
1723
- // } | {
1724
- // name: string;
1725
- // type: 'dog';
1726
- // dogType: string; // Needn't care about this field, because it's not a common pet info field.
1727
- // }
1728
-
1729
- // petInfo type is complex and have some needless fields
1730
-
1731
- console.log('name: ', petInfo.name);
1732
- console.log('type: ', petInfo.type);
1733
- }
1734
-
1735
- function displayPetInfo(petInfo: SharedUnionFieldsDeep<Cat | Dog>['info']) {
1736
- // typeof petInfo =>
1737
- // {
1738
- // name: string;
1739
- // type: 'cat' | 'dog';
1740
- // }
1741
-
1742
- // petInfo type is simple and clear
1751
+ type Numbers = 1 | 2 | 3;
1752
+ type NumbersTuple = UnionToTuple<Numbers>;
1753
+ //=> [1, 2, 3]
1754
+ ```
1743
1755
 
1744
- console.log('name: ', petInfo.name);
1745
- console.log('type: ', petInfo.type);
1746
- }
1756
+ @example
1747
1757
  ```
1758
+ import type {UnionToTuple} from 'type-fest';
1748
1759
 
1749
- @see SharedUnionFields
1760
+ const pets = {
1761
+ dog: '🐶',
1762
+ cat: '🐱',
1763
+ snake: '🐍',
1764
+ };
1750
1765
 
1751
- @category Object
1752
- @category Union
1753
- */
1754
- type SharedUnionFieldsDeep<Union, Options extends SharedUnionFieldsDeepOptions = {recurseIntoArrays: false}> =
1755
- // `Union extends` will convert `Union`
1756
- // to a [distributive conditionaltype](https://www.typescriptlang.org/docs/handbook/release-notes/typescript-2-8.html#distributive-conditional-types).
1757
- // But this is not what we want, so we need to wrap `Union` with `[]` to prevent it.
1758
- [Union] extends [NonRecursiveType | ReadonlyMap<unknown, unknown> | ReadonlySet<unknown>]
1759
- ? Union
1760
- : [Union] extends [UnknownArray]
1761
- ? Options['recurseIntoArrays'] extends true
1762
- ? SetArrayAccess<SharedArrayUnionFieldsDeep<Union, Options>, IsArrayReadonly<Union>>
1763
- : Union
1764
- : [Union] extends [object]
1765
- ? SharedObjectUnionFieldsDeep<Union, Options>
1766
- : Union;
1767
-
1768
- /**
1769
- Same as `SharedUnionFieldsDeep`, but accepts only `object`s and as inputs. Internal helper for `SharedUnionFieldsDeep`.
1770
- */
1771
- type SharedObjectUnionFieldsDeep<Union, Options extends SharedUnionFieldsDeepOptions> =
1772
- // `keyof Union` can extract the same key in union type, if there is no same key, return never.
1773
- keyof Union extends infer Keys
1774
- ? IsNever<Keys> extends false
1775
- ? {
1776
- [Key in keyof Union]:
1777
- Union[Key] extends NonRecursiveType
1778
- ? Union[Key]
1779
- // Remove `undefined` from the union to support optional
1780
- // fields, then recover `undefined` if union was already undefined.
1781
- : SharedUnionFieldsDeep<Exclude<Union[Key], undefined>, Options> | (
1782
- undefined extends Required<Union>[Key] ? undefined : never
1783
- )
1784
- }
1785
- : {}
1786
- : Union;
1766
+ type Pet = keyof typeof pets;
1767
+ //=> 'dog' | 'cat' | 'snake'
1787
1768
 
1788
- /**
1789
- Same as `SharedUnionFieldsDeep`, but accepts only `UnknownArray`s and as inputs. Internal helper for `SharedUnionFieldsDeep`.
1790
- */
1791
- type SharedArrayUnionFieldsDeep<Union extends UnknownArray, Options extends SharedUnionFieldsDeepOptions> =
1792
- // Restore the readonly modifier of the array.
1793
- SetArrayAccess<
1794
- InternalSharedArrayUnionFieldsDeep<Union, Options>,
1795
- IsArrayReadonly<Union>
1796
- >;
1769
+ const petList = Object.keys(pets) as UnionToTuple<Pet>;
1770
+ //=> ['dog', 'cat', 'snake']
1771
+ ```
1797
1772
 
1798
- /**
1799
- Internal helper for `SharedArrayUnionFieldsDeep`. Needn't care the `readonly` modifier of arrays.
1773
+ @category Array
1800
1774
  */
1801
- type InternalSharedArrayUnionFieldsDeep<
1802
- Union extends UnknownArray,
1803
- Options extends SharedUnionFieldsDeepOptions,
1804
- ResultTuple extends UnknownArray = [],
1805
- > =
1806
- // We should build a minimum possible length tuple where each element in the tuple exists in the union tuple.
1807
- IsNever<TupleLength<Union>> extends true
1808
- // Rule 1: If all the arrays in the union have non-fixed lengths,
1809
- // like `Array<string> | [number, ...string[]]`
1810
- // we should build a tuple that is [the_fixed_parts_of_union, ...the_rest_of_union[]].
1811
- // For example: `InternalSharedArrayUnionFieldsDeep<Array<string> | [number, ...string[]]>`
1812
- // => `[string | number, ...string[]]`.
1813
- ? ResultTuple['length'] extends UnionMax<StaticPartOfArray<Union>['length']>
1814
- ? [
1815
- // The fixed-length part of the tuple.
1816
- ...ResultTuple,
1817
- // The rest of the union.
1818
- // Due to `ResultTuple` is the maximum possible fixed-length part of the tuple,
1819
- // so we can use `StaticPartOfArray` to get the rest of the union.
1820
- ...Array<
1821
- SharedUnionFieldsDeep<VariablePartOfArray<Union>[number], Options>
1822
- >,
1823
- ]
1824
- // Build the fixed-length tuple recursively.
1825
- : InternalSharedArrayUnionFieldsDeep<
1826
- Union, Options,
1827
- [...ResultTuple, SharedUnionFieldsDeep<Union[ResultTuple['length']], Options>]
1828
- >
1829
- // Rule 2: If at least one of the arrays in the union have fixed lengths,
1830
- // like `Array<string> | [number, string]`,
1831
- // we should build a tuple of the smallest possible length to ensure any
1832
- // item in the result tuple exists in the union tuple.
1833
- // For example: `InternalSharedArrayUnionFieldsDeep<Array<string> | [number, string]>`
1834
- // => `[string | number, string]`.
1835
- : ResultTuple['length'] extends UnionMin<TupleLength<Union>>
1836
- ? ResultTuple
1837
- // As above, build tuple recursively.
1838
- : InternalSharedArrayUnionFieldsDeep<
1839
- Union, Options,
1840
- [...ResultTuple, SharedUnionFieldsDeep<Union[ResultTuple['length']], Options>]
1841
- >;
1775
+ type UnionToTuple<T, L = LastOfUnion<T>> =
1776
+ IsNever<T> extends false
1777
+ ? [...UnionToTuple<Exclude<T, L>>, L]
1778
+ : [];
1842
1779
 
1843
1780
  /**
1844
1781
  Omit properties from a deeply-nested object.
@@ -1924,11 +1861,19 @@ type AddressInfo = OmitDeep<Info1, 'address.1.foo'>;
1924
1861
  */
1925
1862
  type OmitDeep<T, PathUnion extends LiteralUnion<Paths<T>, string>> =
1926
1863
  SimplifyDeep<
1927
- SharedUnionFieldsDeep<
1928
- {[P in PathUnion]: OmitDeepWithOnePath<T, P>}[PathUnion]
1929
- >,
1864
+ OmitDeepHelper<T, UnionToTuple<PathUnion>>,
1930
1865
  UnknownArray>;
1931
1866
 
1867
+ /**
1868
+ Internal helper for {@link OmitDeep}.
1869
+
1870
+ Recursively transforms `T` by applying {@link OmitDeepWithOnePath} for each path in `PathTuple`.
1871
+ */
1872
+ type OmitDeepHelper<T, PathTuple extends UnknownArray> =
1873
+ PathTuple extends [infer Path, ...infer RestPaths]
1874
+ ? OmitDeepHelper<OmitDeepWithOnePath<T, Path & (string | number)>, RestPaths>
1875
+ : T;
1876
+
1932
1877
  /**
1933
1878
  Omit one path from the given object/array.
1934
1879
  */
@@ -2016,6 +1961,41 @@ type StringKeysOfFoo = StringKeyOf<Foo>;
2016
1961
  */
2017
1962
  type StringKeyOf<BaseType> = `${Extract<keyof BaseType, string | number>}`;
2018
1963
 
1964
+ /**
1965
+ Split options.
1966
+
1967
+ @see {@link Split}
1968
+ */
1969
+ type SplitOptions = {
1970
+ /**
1971
+ When enabled, instantiations with non-literal string types (e.g., `string`, `Uppercase<string>`, `on${string}`) simply return back `string[]` without performing any splitting, as the exact structure cannot be statically determined.
1972
+
1973
+ Note: In the future, this option might be enabled by default, so if you currently rely on this being disabled, you should consider explicitly enabling it.
1974
+
1975
+ @default false
1976
+
1977
+ @example
1978
+ ```ts
1979
+ type Example1 = Split<`foo.${string}.bar`, '.', {strictLiteralChecks: false}>;
1980
+ //=> ['foo', string, 'bar']
1981
+
1982
+ type Example2 = Split<`foo.${string}`, '.', {strictLiteralChecks: true}>;
1983
+ //=> string[]
1984
+
1985
+ type Example3 = Split<'foobarbaz', `b${string}`, {strictLiteralChecks: false}>;
1986
+ //=> ['foo', 'r', 'z']
1987
+
1988
+ type Example4 = Split<'foobarbaz', `b${string}`, {strictLiteralChecks: true}>;
1989
+ //=> string[]
1990
+ ```
1991
+ */
1992
+ strictLiteralChecks?: boolean;
1993
+ };
1994
+
1995
+ type DefaultSplitOptions = {
1996
+ strictLiteralChecks: false;
1997
+ };
1998
+
2019
1999
  /**
2020
2000
  Represents an array of strings split using a given character or character set.
2021
2001
 
@@ -2034,17 +2014,37 @@ let array: Item[];
2034
2014
  array = split(items, ',');
2035
2015
  ```
2036
2016
 
2017
+ @see {@link SplitOptions}
2018
+
2037
2019
  @category String
2038
2020
  @category Template literal
2039
2021
  */
2040
2022
  type Split<
2041
2023
  S extends string,
2042
2024
  Delimiter extends string,
2043
- > = S extends `${infer Head}${Delimiter}${infer Tail}`
2044
- ? [Head, ...Split<Tail, Delimiter>]
2045
- : S extends Delimiter
2046
- ? []
2047
- : [S];
2025
+ Options extends SplitOptions = {},
2026
+ > = SplitHelper<S, Delimiter, {
2027
+ strictLiteralChecks: Options['strictLiteralChecks'] extends boolean ? Options['strictLiteralChecks'] : DefaultSplitOptions['strictLiteralChecks'];
2028
+ }>;
2029
+
2030
+ type SplitHelper<
2031
+ S extends string,
2032
+ Delimiter extends string,
2033
+ Options extends Required<SplitOptions>,
2034
+ Accumulator extends string[] = [],
2035
+ > = S extends string // For distributing `S`
2036
+ ? Delimiter extends string // For distributing `Delimeter`
2037
+ // If `strictLiteralChecks` is `false` OR `S` and `Delimiter` both are string literals, then perform the split
2038
+ ? Or<Not<Options['strictLiteralChecks']>, And<IsStringLiteral<S>, IsStringLiteral<Delimiter>>> extends true
2039
+ ? S extends `${infer Head}${Delimiter}${infer Tail}`
2040
+ ? SplitHelper<Tail, Delimiter, Options, [...Accumulator, Head]>
2041
+ : Delimiter extends ''
2042
+ ? Accumulator
2043
+ : [...Accumulator, S]
2044
+ // Otherwise, return `string[]`
2045
+ : string[]
2046
+ : never // Should never happen
2047
+ : never; // Should never happen
2048
2048
 
2049
2049
  type GetOptions = {
2050
2050
  /**
@@ -2247,7 +2247,7 @@ type Get<
2247
2247
  BaseType,
2248
2248
  Path extends
2249
2249
  | readonly string[]
2250
- | LiteralStringUnion<ToString<Paths<BaseType, {bracketNotation: false}> | Paths<BaseType, {bracketNotation: true}>>>,
2250
+ | LiteralStringUnion<ToString<Paths<BaseType, {bracketNotation: false; maxRecursionDepth: 2}> | Paths<BaseType, {bracketNotation: true; maxRecursionDepth: 2}>>>,
2251
2251
  Options extends GetOptions = {}> =
2252
2252
  GetWithPath<BaseType, Path extends string ? ToPath<Path> : Path, Options>;
2253
2253