@visulima/object 1.0.9 → 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.ts CHANGED
@@ -452,6 +452,71 @@ type ShouldBeTrue = IsNegative<-1>;
452
452
  */
453
453
  type IsNegative<T extends Numeric> = T extends Negative<T> ? true : false;
454
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
+
455
520
  /**
456
521
  Returns a boolean for whether two given types are both true.
457
522
 
@@ -588,32 +653,7 @@ type LessThan<A extends number, B extends number> = number extends A | B
588
653
  ? never
589
654
  : GreaterThanOrEqual<A, B> extends true ? false : true;
590
655
 
591
- /**
592
- Infer the length of the given tuple `<T>`.
593
-
594
- Returns `never` if the given type is an non-fixed-length array like `Array<string>`.
595
-
596
- @example
597
- ```
598
- type Tuple = TupleLength<[string, number, boolean]>;
599
- //=> 3
600
-
601
- type Array = TupleLength<string[]>;
602
- //=> never
603
-
604
- // Supports union types.
605
- type Union = TupleLength<[] | [1, 2, 3] | Array<number>>;
606
- //=> 1 | 3
607
- ```
608
- */
609
- type TupleLength<T extends UnknownArray> =
610
- // `extends unknown` is used to convert `T` (if `T` is a union type) to
611
- // a [distributive conditionaltype](https://www.typescriptlang.org/docs/handbook/release-notes/typescript-2-8.html#distributive-conditional-types))
612
- T extends unknown
613
- ? number extends T['length']
614
- ? never // Return never if the given type is an non-flexed-length array like `Array<string>`
615
- : T['length']
616
- : never; // Should never happen
656
+ // Should never happen
617
657
 
618
658
  /**
619
659
  Create a tuple type of the given length `<L>` and fill it with the given type `<Fill>`.
@@ -628,52 +668,6 @@ type BuildTuple<L extends number, Fill = unknown, T extends readonly unknown[] =
628
668
  ? T
629
669
  : BuildTuple<L, Fill, [...T, Fill]>;
630
670
 
631
- /**
632
- Returns the maximum value from a tuple of integers.
633
-
634
- Note:
635
- - Float numbers are not supported.
636
-
637
- @example
638
- ```
639
- ArrayMax<[1, 2, 5, 3]>;
640
- //=> 5
641
-
642
- ArrayMax<[1, 2, 5, 3, 99, -1]>;
643
- //=> 99
644
- ```
645
- */
646
- type TupleMax<A extends number[], Result extends number = NegativeInfinity> = number extends A[number]
647
- ? never :
648
- A extends [infer F extends number, ...infer R extends number[]]
649
- ? GreaterThan<F, Result> extends true
650
- ? TupleMax<R, F>
651
- : TupleMax<R, Result>
652
- : Result;
653
-
654
- /**
655
- Returns the minimum value from a tuple of integers.
656
-
657
- Note:
658
- - Float numbers are not supported.
659
-
660
- @example
661
- ```
662
- ArrayMin<[1, 2, 5, 3]>;
663
- //=> 1
664
-
665
- ArrayMin<[1, 2, 5, 3, -5]>;
666
- //=> -5
667
- ```
668
- */
669
- type TupleMin<A extends number[], Result extends number = PositiveInfinity> = number extends A[number]
670
- ? never
671
- : A extends [infer F extends number, ...infer R extends number[]]
672
- ? LessThan<F, Result> extends true
673
- ? TupleMin<R, F>
674
- : TupleMin<R, Result>
675
- : Result;
676
-
677
671
  /**
678
672
  Return a string representation of the given string or number.
679
673
 
@@ -904,48 +898,30 @@ type IsNumberLike<N> =
904
898
  : false;
905
899
 
906
900
  /**
907
- Returns the minimum number in the given union of numbers.
908
-
909
- Note: Just supports numbers from 0 to 999.
901
+ Returns the number with reversed sign.
910
902
 
911
903
  @example
912
904
  ```
913
- type A = UnionMin<3 | 1 | 2>;
905
+ ReverseSign<-1>;
914
906
  //=> 1
915
- ```
916
- */
917
- type UnionMin<N extends number> = InternalUnionMin<N>;
918
-
919
- /**
920
- The actual implementation of `UnionMin`. It's private because it has some arguments that don't need to be exposed.
921
- */
922
- type InternalUnionMin<N extends number, T extends UnknownArray = []> =
923
- T['length'] extends N
924
- ? T['length']
925
- : InternalUnionMin<N, [...T, unknown]>;
926
907
 
927
- /**
928
- Returns the maximum number in the given union of numbers.
908
+ ReverseSign<1>;
909
+ //=> -1
929
910
 
930
- Note: Just supports numbers from 0 to 999.
911
+ ReverseSign<NegativeInfinity>
912
+ //=> PositiveInfinity
931
913
 
932
- @example
933
- ```
934
- type A = UnionMax<1 | 3 | 2>;
935
- //=> 3
914
+ ReverseSign<PositiveInfinity>
915
+ //=> NegativeInfinity
936
916
  ```
937
917
  */
938
- type UnionMax<N extends number> = InternalUnionMax<N>;
939
-
940
- /**
941
- The actual implementation of `UnionMax`. It's private because it has some arguments that don't need to be exposed.
942
- */
943
- type InternalUnionMax<N extends number, T extends UnknownArray = []> =
944
- IsNever<N> extends true
945
- ? T['length']
946
- : T['length'] extends N
947
- ? InternalUnionMax<Exclude<N, T['length']>, T>
948
- : 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;
949
925
 
950
926
  /**
951
927
  Matches any primitive, `void`, `Date`, or `RegExp` value.
@@ -957,6 +933,24 @@ Matches non-recursive types.
957
933
  */
958
934
  type NonRecursiveType = BuiltIns | Function | (new (...arguments_: any[]) => unknown);
959
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
+
960
954
  /**
961
955
  Create an object type with the given key `<Key>` and value `<Value>`.
962
956
 
@@ -1110,76 +1104,11 @@ type SimplifyDeep<Type, ExcludeType = never> =
1110
1104
  object
1111
1105
  >;
1112
1106
 
1113
- /**
1114
- Returns the sum of two numbers.
1115
-
1116
- Note:
1117
- - A or B can only support `-999` ~ `999`.
1118
- - A and B can only be small integers, less than 1000.
1119
- - If the result is negative, you can only get `number`.
1120
-
1121
- @example
1122
- ```
1123
- import type {Sum} from 'type-fest';
1124
-
1125
- Sum<111, 222>;
1126
- //=> 333
1127
-
1128
- Sum<-111, 222>;
1129
- //=> 111
1130
-
1131
- Sum<111, -222>;
1132
- //=> number
1133
-
1134
- Sum<PositiveInfinity, -9999>;
1135
- //=> PositiveInfinity
1136
-
1137
- Sum<PositiveInfinity, NegativeInfinity>;
1138
- //=> number
1139
- ```
1140
-
1141
- @category Numeric
1142
- */
1143
- // TODO: Support big integer and negative number.
1144
- type Sum<A extends number, B extends number> = number extends A | B
1145
- ? number
1146
- : [
1147
- IsEqual<A, PositiveInfinity>, IsEqual<A, NegativeInfinity>,
1148
- IsEqual<B, PositiveInfinity>, IsEqual<B, NegativeInfinity>,
1149
- ] extends infer R extends [boolean, boolean, boolean, boolean]
1150
- ? Or<
1151
- And<IsEqual<R[0], true>, IsEqual<R[3], false>>,
1152
- And<IsEqual<R[2], true>, IsEqual<R[1], false>>
1153
- > extends true
1154
- ? PositiveInfinity
1155
- : Or<
1156
- And<IsEqual<R[1], true>, IsEqual<R[2], false>>,
1157
- And<IsEqual<R[3], true>, IsEqual<R[0], false>>
1158
- > extends true
1159
- ? NegativeInfinity
1160
- : true extends R[number]
1161
- ? number
1162
- : ([IsNegative<A>, IsNegative<B>] extends infer R
1163
- ? [false, false] extends R
1164
- ? [...BuildTuple<A>, ...BuildTuple<B>]['length']
1165
- : [true, true] extends R
1166
- ? number
1167
- : TupleMax<[NumberAbsolute<A>, NumberAbsolute<B>]> extends infer Max_
1168
- ? TupleMin<[NumberAbsolute<A>, NumberAbsolute<B>]> extends infer Min_ extends number
1169
- ? Max_ extends A | B
1170
- ? Subtract<Max_, Min_>
1171
- : number
1172
- : never
1173
- : never
1174
- : never) & number
1175
- : never;
1176
-
1177
1107
  /**
1178
1108
  Returns the difference between two numbers.
1179
1109
 
1180
1110
  Note:
1181
1111
  - A or B can only support `-999` ~ `999`.
1182
- - If the result is negative, you can only get `number`.
1183
1112
 
1184
1113
  @example
1185
1114
  ```
@@ -1192,7 +1121,10 @@ Subtract<111, -222>;
1192
1121
  //=> 333
1193
1122
 
1194
1123
  Subtract<-111, 222>;
1195
- //=> number
1124
+ //=> -333
1125
+
1126
+ Subtract<18, 96>;
1127
+ //=> -78
1196
1128
 
1197
1129
  Subtract<PositiveInfinity, 9999>;
1198
1130
  //=> PositiveInfinity
@@ -1203,38 +1135,53 @@ Subtract<PositiveInfinity, PositiveInfinity>;
1203
1135
 
1204
1136
  @category Numeric
1205
1137
  */
1206
- // TODO: Support big integer and negative number.
1207
- type Subtract<A extends number, B extends number> = number extends A | B
1208
- ? number
1209
- : [
1210
- IsEqual<A, PositiveInfinity>, IsEqual<A, NegativeInfinity>,
1211
- IsEqual<B, PositiveInfinity>, IsEqual<B, NegativeInfinity>,
1212
- ] extends infer R extends [boolean, boolean, boolean, boolean]
1213
- ? Or<
1214
- And<IsEqual<R[0], true>, IsEqual<R[2], false>>,
1215
- And<IsEqual<R[3], true>, IsEqual<R[1], false>>
1216
- > extends true
1217
- ? PositiveInfinity
1218
- : Or<
1219
- And<IsEqual<R[1], true>, IsEqual<R[3], false>>,
1220
- And<IsEqual<R[2], true>, IsEqual<R[0], false>>
1221
- > extends true
1222
- ? NegativeInfinity
1223
- : true extends R[number]
1224
- ? number
1225
- : [IsNegative<A>, IsNegative<B>] extends infer R
1226
- ? [false, false] extends R
1227
- ? BuildTuple<A> extends infer R
1228
- ? R extends [...BuildTuple<B>, ...infer R]
1229
- ? R['length']
1230
- : number
1231
- : never
1232
- : LessThan<A, B> extends true
1233
- ? number
1234
- : [false, true] extends R
1235
- ? Sum<A, NumberAbsolute<B>>
1236
- : Subtract<NumberAbsolute<B>, NumberAbsolute<A>>
1237
- : 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']
1238
1185
  : never;
1239
1186
 
1240
1187
  /**
@@ -1282,11 +1229,89 @@ type PathsOptions = {
1282
1229
  ```
1283
1230
  */
1284
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;
1285
1308
  };
1286
1309
 
1287
1310
  type DefaultPathsOptions = {
1288
1311
  maxRecursionDepth: 10;
1289
1312
  bracketNotation: false;
1313
+ leavesOnly: false;
1314
+ depth: number;
1290
1315
  };
1291
1316
 
1292
1317
  /**
@@ -1335,6 +1360,10 @@ type Paths<T, Options extends PathsOptions = {}> = _Paths<T, {
1335
1360
  maxRecursionDepth: Options['maxRecursionDepth'] extends number ? Options['maxRecursionDepth'] : DefaultPathsOptions['maxRecursionDepth'];
1336
1361
  // Set default bracketNotation to false
1337
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'];
1338
1367
  }>;
1339
1368
 
1340
1369
  type _Paths<T, Options extends Required<PathsOptions>> =
@@ -1374,11 +1403,30 @@ type InternalPaths<T, Options extends Required<PathsOptions>> =
1374
1403
  ) extends infer TranformedKey extends string | number ?
1375
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
1376
1405
  // 2. If style is 'a.0.b', transform 'Key' to `${Key}` | Key
1377
- | 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)
1378
1420
  | (
1379
1421
  // Recursively generate paths for the current key
1380
1422
  GreaterThan<MaxDepth, 0> extends true // Limit the depth to prevent infinite recursion
1381
- ? _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
1382
1430
  ? SubPath extends string | number
1383
1431
  ? (
1384
1432
  Options['bracketNotation'] extends true
@@ -1562,7 +1610,9 @@ The implementation of `SplitArrayByIndex` for variable length arrays.
1562
1610
  type SplitVariableArrayByIndex<T extends UnknownArray,
1563
1611
  SplitIndex extends number,
1564
1612
  T1 = Subtract<SplitIndex, StaticPartOfArray<T>['length']>,
1565
- 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
+ : [],
1566
1616
  > =
1567
1617
  SplitIndex extends 0
1568
1618
  ? [[], T]
@@ -1674,173 +1724,58 @@ type LiteralUnion<
1674
1724
  > = LiteralType | (BaseType & Record<never, never>);
1675
1725
 
1676
1726
  /**
1677
- SharedUnionFieldsDeep options.
1727
+ Returns the last element of a union type.
1678
1728
 
1679
- @see {@link SharedUnionFieldsDeep}
1729
+ @example
1730
+ ```
1731
+ type Last = LastOfUnion<1 | 2 | 3>;
1732
+ //=> 3
1733
+ ```
1680
1734
  */
1681
- type SharedUnionFieldsDeepOptions = {
1682
- /**
1683
- 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.
1684
-
1685
- @default false
1686
- */
1687
- recurseIntoArrays?: boolean;
1688
- };
1735
+ type LastOfUnion<T> =
1736
+ UnionToIntersection<T extends any ? () => T : never> extends () => (infer R)
1737
+ ? R
1738
+ : never;
1689
1739
 
1690
1740
  /**
1691
- 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.
1692
1742
 
1693
- 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.
1694
1744
 
1695
- Use-cases:
1696
- - You want a safe object type where each key exists in the union object.
1697
- - 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.
1698
1746
 
1699
1747
  @example
1700
1748
  ```
1701
- import type {SharedUnionFieldsDeep} from 'type-fest';
1702
-
1703
- type Cat = {
1704
- info: {
1705
- name: string;
1706
- type: 'cat';
1707
- catType: string;
1708
- };
1709
- };
1710
-
1711
- type Dog = {
1712
- info: {
1713
- name: string;
1714
- type: 'dog';
1715
- dogType: string;
1716
- };
1717
- };
1718
-
1719
- function displayPetInfo(petInfo: (Cat | Dog)['info']) {
1720
- // typeof petInfo =>
1721
- // {
1722
- // name: string;
1723
- // type: 'cat';
1724
- // catType: string; // Needn't care about this field, because it's not a common pet info field.
1725
- // } | {
1726
- // name: string;
1727
- // type: 'dog';
1728
- // dogType: string; // Needn't care about this field, because it's not a common pet info field.
1729
- // }
1730
-
1731
- // petInfo type is complex and have some needless fields
1732
-
1733
- console.log('name: ', petInfo.name);
1734
- console.log('type: ', petInfo.type);
1735
- }
1736
-
1737
- function displayPetInfo(petInfo: SharedUnionFieldsDeep<Cat | Dog>['info']) {
1738
- // typeof petInfo =>
1739
- // {
1740
- // name: string;
1741
- // type: 'cat' | 'dog';
1742
- // }
1749
+ import type {UnionToTuple} from 'type-fest';
1743
1750
 
1744
- // petInfo type is simple and clear
1751
+ type Numbers = 1 | 2 | 3;
1752
+ type NumbersTuple = UnionToTuple<Numbers>;
1753
+ //=> [1, 2, 3]
1754
+ ```
1745
1755
 
1746
- console.log('name: ', petInfo.name);
1747
- console.log('type: ', petInfo.type);
1748
- }
1756
+ @example
1749
1757
  ```
1758
+ import type {UnionToTuple} from 'type-fest';
1750
1759
 
1751
- @see SharedUnionFields
1760
+ const pets = {
1761
+ dog: '🐶',
1762
+ cat: '🐱',
1763
+ snake: '🐍',
1764
+ };
1752
1765
 
1753
- @category Object
1754
- @category Union
1755
- */
1756
- type SharedUnionFieldsDeep<Union, Options extends SharedUnionFieldsDeepOptions = {recurseIntoArrays: false}> =
1757
- // `Union extends` will convert `Union`
1758
- // to a [distributive conditionaltype](https://www.typescriptlang.org/docs/handbook/release-notes/typescript-2-8.html#distributive-conditional-types).
1759
- // But this is not what we want, so we need to wrap `Union` with `[]` to prevent it.
1760
- [Union] extends [NonRecursiveType | ReadonlyMap<unknown, unknown> | ReadonlySet<unknown>]
1761
- ? Union
1762
- : [Union] extends [UnknownArray]
1763
- ? Options['recurseIntoArrays'] extends true
1764
- ? SetArrayAccess<SharedArrayUnionFieldsDeep<Union, Options>, IsArrayReadonly<Union>>
1765
- : Union
1766
- : [Union] extends [object]
1767
- ? SharedObjectUnionFieldsDeep<Union, Options>
1768
- : Union;
1769
-
1770
- /**
1771
- Same as `SharedUnionFieldsDeep`, but accepts only `object`s and as inputs. Internal helper for `SharedUnionFieldsDeep`.
1772
- */
1773
- type SharedObjectUnionFieldsDeep<Union, Options extends SharedUnionFieldsDeepOptions> =
1774
- // `keyof Union` can extract the same key in union type, if there is no same key, return never.
1775
- keyof Union extends infer Keys
1776
- ? IsNever<Keys> extends false
1777
- ? {
1778
- [Key in keyof Union]:
1779
- Union[Key] extends NonRecursiveType
1780
- ? Union[Key]
1781
- // Remove `undefined` from the union to support optional
1782
- // fields, then recover `undefined` if union was already undefined.
1783
- : SharedUnionFieldsDeep<Exclude<Union[Key], undefined>, Options> | (
1784
- undefined extends Required<Union>[Key] ? undefined : never
1785
- )
1786
- }
1787
- : {}
1788
- : Union;
1766
+ type Pet = keyof typeof pets;
1767
+ //=> 'dog' | 'cat' | 'snake'
1789
1768
 
1790
- /**
1791
- Same as `SharedUnionFieldsDeep`, but accepts only `UnknownArray`s and as inputs. Internal helper for `SharedUnionFieldsDeep`.
1792
- */
1793
- type SharedArrayUnionFieldsDeep<Union extends UnknownArray, Options extends SharedUnionFieldsDeepOptions> =
1794
- // Restore the readonly modifier of the array.
1795
- SetArrayAccess<
1796
- InternalSharedArrayUnionFieldsDeep<Union, Options>,
1797
- IsArrayReadonly<Union>
1798
- >;
1769
+ const petList = Object.keys(pets) as UnionToTuple<Pet>;
1770
+ //=> ['dog', 'cat', 'snake']
1771
+ ```
1799
1772
 
1800
- /**
1801
- Internal helper for `SharedArrayUnionFieldsDeep`. Needn't care the `readonly` modifier of arrays.
1773
+ @category Array
1802
1774
  */
1803
- type InternalSharedArrayUnionFieldsDeep<
1804
- Union extends UnknownArray,
1805
- Options extends SharedUnionFieldsDeepOptions,
1806
- ResultTuple extends UnknownArray = [],
1807
- > =
1808
- // We should build a minimum possible length tuple where each element in the tuple exists in the union tuple.
1809
- IsNever<TupleLength<Union>> extends true
1810
- // Rule 1: If all the arrays in the union have non-fixed lengths,
1811
- // like `Array<string> | [number, ...string[]]`
1812
- // we should build a tuple that is [the_fixed_parts_of_union, ...the_rest_of_union[]].
1813
- // For example: `InternalSharedArrayUnionFieldsDeep<Array<string> | [number, ...string[]]>`
1814
- // => `[string | number, ...string[]]`.
1815
- ? ResultTuple['length'] extends UnionMax<StaticPartOfArray<Union>['length']>
1816
- ? [
1817
- // The fixed-length part of the tuple.
1818
- ...ResultTuple,
1819
- // The rest of the union.
1820
- // Due to `ResultTuple` is the maximum possible fixed-length part of the tuple,
1821
- // so we can use `StaticPartOfArray` to get the rest of the union.
1822
- ...Array<
1823
- SharedUnionFieldsDeep<VariablePartOfArray<Union>[number], Options>
1824
- >,
1825
- ]
1826
- // Build the fixed-length tuple recursively.
1827
- : InternalSharedArrayUnionFieldsDeep<
1828
- Union, Options,
1829
- [...ResultTuple, SharedUnionFieldsDeep<Union[ResultTuple['length']], Options>]
1830
- >
1831
- // Rule 2: If at least one of the arrays in the union have fixed lengths,
1832
- // like `Array<string> | [number, string]`,
1833
- // we should build a tuple of the smallest possible length to ensure any
1834
- // item in the result tuple exists in the union tuple.
1835
- // For example: `InternalSharedArrayUnionFieldsDeep<Array<string> | [number, string]>`
1836
- // => `[string | number, string]`.
1837
- : ResultTuple['length'] extends UnionMin<TupleLength<Union>>
1838
- ? ResultTuple
1839
- // As above, build tuple recursively.
1840
- : InternalSharedArrayUnionFieldsDeep<
1841
- Union, Options,
1842
- [...ResultTuple, SharedUnionFieldsDeep<Union[ResultTuple['length']], Options>]
1843
- >;
1775
+ type UnionToTuple<T, L = LastOfUnion<T>> =
1776
+ IsNever<T> extends false
1777
+ ? [...UnionToTuple<Exclude<T, L>>, L]
1778
+ : [];
1844
1779
 
1845
1780
  /**
1846
1781
  Omit properties from a deeply-nested object.
@@ -1926,11 +1861,19 @@ type AddressInfo = OmitDeep<Info1, 'address.1.foo'>;
1926
1861
  */
1927
1862
  type OmitDeep<T, PathUnion extends LiteralUnion<Paths<T>, string>> =
1928
1863
  SimplifyDeep<
1929
- SharedUnionFieldsDeep<
1930
- {[P in PathUnion]: OmitDeepWithOnePath<T, P>}[PathUnion]
1931
- >,
1864
+ OmitDeepHelper<T, UnionToTuple<PathUnion>>,
1932
1865
  UnknownArray>;
1933
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
+
1934
1877
  /**
1935
1878
  Omit one path from the given object/array.
1936
1879
  */
@@ -2018,6 +1961,41 @@ type StringKeysOfFoo = StringKeyOf<Foo>;
2018
1961
  */
2019
1962
  type StringKeyOf<BaseType> = `${Extract<keyof BaseType, string | number>}`;
2020
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
+
2021
1999
  /**
2022
2000
  Represents an array of strings split using a given character or character set.
2023
2001
 
@@ -2036,23 +2014,37 @@ let array: Item[];
2036
2014
  array = split(items, ',');
2037
2015
  ```
2038
2016
 
2017
+ @see {@link SplitOptions}
2018
+
2039
2019
  @category String
2040
2020
  @category Template literal
2041
2021
  */
2042
2022
  type Split<
2043
2023
  S extends string,
2044
2024
  Delimiter extends string,
2045
- > = SplitHelper<S, Delimiter>;
2025
+ Options extends SplitOptions = {},
2026
+ > = SplitHelper<S, Delimiter, {
2027
+ strictLiteralChecks: Options['strictLiteralChecks'] extends boolean ? Options['strictLiteralChecks'] : DefaultSplitOptions['strictLiteralChecks'];
2028
+ }>;
2046
2029
 
2047
2030
  type SplitHelper<
2048
2031
  S extends string,
2049
2032
  Delimiter extends string,
2033
+ Options extends Required<SplitOptions>,
2050
2034
  Accumulator extends string[] = [],
2051
- > = S extends `${infer Head}${Delimiter}${infer Tail}`
2052
- ? SplitHelper<Tail, Delimiter, [...Accumulator, Head]>
2053
- : Delimiter extends ''
2054
- ? Accumulator
2055
- : [...Accumulator, S];
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
2056
2048
 
2057
2049
  type GetOptions = {
2058
2050
  /**