@depup/type-fest 5.5.0-depup.0 → 5.10.0-depup.0

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.
Files changed (120) hide show
  1. package/README.md +2 -2
  2. package/changes.json +1 -1
  3. package/index.d.ts +17 -8
  4. package/package.json +9 -6
  5. package/readme.md +75 -61
  6. package/source/absolute.d.ts +52 -0
  7. package/source/all-extend.d.ts +9 -5
  8. package/source/all-union-fields.d.ts +18 -18
  9. package/source/and.d.ts +1 -1
  10. package/source/array-reverse.d.ts +17 -15
  11. package/source/array-splice.d.ts +24 -24
  12. package/source/array-tail.d.ts +7 -7
  13. package/source/camel-case.d.ts +38 -5
  14. package/source/camel-cased-properties-deep.d.ts +11 -4
  15. package/source/camel-cased-properties.d.ts +5 -1
  16. package/source/conditional-keys.d.ts +1 -1
  17. package/source/delimiter-case.d.ts +11 -9
  18. package/source/delimiter-cased-properties-deep.d.ts +8 -1
  19. package/source/delimiter-cased-properties.d.ts +5 -1
  20. package/source/empty-object.d.ts +1 -1
  21. package/source/entries.d.ts +1 -1
  22. package/source/entry.d.ts +1 -1
  23. package/source/exclude-exactly.d.ts +11 -11
  24. package/source/exclude-rest-element.d.ts +4 -4
  25. package/source/exclusify-union.d.ts +4 -4
  26. package/source/extends-strict.d.ts +129 -22
  27. package/source/extract-exactly.d.ts +56 -0
  28. package/source/get.d.ts +1 -1
  29. package/source/greater-than.d.ts +3 -2
  30. package/source/has-optional-keys.d.ts +1 -1
  31. package/source/has-readonly-keys.d.ts +1 -1
  32. package/source/has-required-keys.d.ts +1 -1
  33. package/source/has-writable-keys.d.ts +1 -1
  34. package/source/int-closed-range.d.ts +5 -4
  35. package/source/int-range.d.ts +57 -12
  36. package/source/internal/array.d.ts +22 -21
  37. package/source/internal/keys.d.ts +9 -9
  38. package/source/internal/numeric.d.ts +19 -65
  39. package/source/internal/object.d.ts +44 -6
  40. package/source/internal/string.d.ts +1 -76
  41. package/source/internal/tuple.d.ts +2 -2
  42. package/source/internal/type.d.ts +16 -10
  43. package/source/is-boolean-literal.d.ts +39 -0
  44. package/source/is-integer.d.ts +8 -8
  45. package/source/is-literal.d.ts +43 -267
  46. package/source/is-numeric-literal.d.ts +50 -0
  47. package/source/is-string-literal.d.ts +72 -0
  48. package/source/is-symbol-literal.d.ts +39 -0
  49. package/source/is-tuple.d.ts +3 -3
  50. package/source/is-union.d.ts +12 -12
  51. package/source/iterable-element.d.ts +5 -5
  52. package/source/jsonify.d.ts +5 -9
  53. package/source/kebab-case.d.ts +1 -0
  54. package/source/kebab-cased-properties-deep.d.ts +7 -0
  55. package/source/kebab-cased-properties.d.ts +5 -1
  56. package/source/keys-of-union.d.ts +2 -2
  57. package/source/last-array-element.d.ts +66 -13
  58. package/source/less-than-or-equal.d.ts +1 -1
  59. package/source/literal-to-primitive.d.ts +1 -1
  60. package/source/literal-union.d.ts +1 -1
  61. package/source/merge-deep.d.ts +3 -3
  62. package/source/merge-exclusive.d.ts +3 -3
  63. package/source/multidimensional-array.d.ts +1 -1
  64. package/source/multidimensional-readonly-array.d.ts +1 -1
  65. package/source/non-nullable-deep.d.ts +102 -0
  66. package/source/numeric.d.ts +3 -3
  67. package/source/object-merge.d.ts +21 -16
  68. package/source/omit-deep.d.ts +22 -22
  69. package/source/or.d.ts +1 -1
  70. package/source/package-json.d.ts +7 -7
  71. package/source/partial-deep.d.ts +3 -1
  72. package/source/pascal-case.d.ts +1 -0
  73. package/source/pascal-cased-properties-deep.d.ts +7 -0
  74. package/source/pascal-cased-properties.d.ts +5 -1
  75. package/source/pick-deep.d.ts +2 -0
  76. package/source/readonly-deep.d.ts +5 -3
  77. package/source/remove-prefix.d.ts +16 -34
  78. package/source/remove-suffix.d.ts +114 -0
  79. package/source/rename-keys.d.ts +163 -0
  80. package/source/replace.d.ts +2 -2
  81. package/source/require-all-or-none.d.ts +5 -5
  82. package/source/require-at-least-one.d.ts +9 -11
  83. package/source/require-exactly-one.d.ts +7 -7
  84. package/source/require-one-or-none.d.ts +5 -5
  85. package/source/required-deep.d.ts +3 -1
  86. package/source/schema.d.ts +16 -8
  87. package/source/screaming-snake-case.d.ts +1 -0
  88. package/source/set-non-nullable-deep.d.ts +8 -4
  89. package/source/set-non-nullable.d.ts +1 -1
  90. package/source/set-optional.d.ts +5 -5
  91. package/source/set-readonly.d.ts +3 -3
  92. package/source/set-required-deep.d.ts +3 -2
  93. package/source/set-required.d.ts +13 -12
  94. package/source/shared-union-fields-deep.d.ts +6 -4
  95. package/source/shared-union-fields.d.ts +9 -9
  96. package/source/snake-case.d.ts +1 -0
  97. package/source/snake-cased-properties-deep.d.ts +7 -0
  98. package/source/snake-cased-properties.d.ts +5 -1
  99. package/source/some-extend.d.ts +9 -5
  100. package/source/split-on-rest-element.d.ts +20 -17
  101. package/source/split.d.ts +22 -11
  102. package/source/string-length.d.ts +38 -0
  103. package/source/string-repeat.d.ts +48 -21
  104. package/source/string-slice.d.ts +1 -1
  105. package/source/string-to-array.d.ts +97 -0
  106. package/source/string-to-number.d.ts +67 -0
  107. package/source/subtract.d.ts +4 -3
  108. package/source/sum.d.ts +5 -4
  109. package/source/tagged.d.ts +3 -5
  110. package/source/tsconfig-json.d.ts +40 -8
  111. package/source/tuple-of.d.ts +42 -8
  112. package/source/typed-array.d.ts +6 -0
  113. package/source/union-length.d.ts +27 -0
  114. package/source/union-max.d.ts +56 -0
  115. package/source/union-to-intersection.d.ts +1 -1
  116. package/source/union-to-tuple.d.ts +8 -4
  117. package/source/unwrap-required.d.ts +37 -0
  118. package/source/words.d.ts +30 -4
  119. package/source/writable.d.ts +14 -14
  120. package/source/xor.d.ts +1 -1
@@ -5,7 +5,7 @@ import type {IsAny} from './is-any.d.ts';
5
5
  import type {IsNever} from './is-never.d.ts';
6
6
 
7
7
  /**
8
- Create a type that requires exactly one of the given keys and disallows more, or none of the given keys. The remaining keys are kept as is.
8
+ Create a type that requires exactly one of the given keys or none of the given keys, while keeping the remaining keys as is.
9
9
 
10
10
  @example
11
11
  ```
@@ -35,11 +35,11 @@ const responder3: Responder = {
35
35
  @category Object
36
36
  */
37
37
  export type RequireOneOrNone<ObjectType, KeysType extends keyof ObjectType = keyof ObjectType> =
38
- IfNotAnyOrNever<ObjectType,
39
- If<IsNever<KeysType>,
38
+ IfNotAnyOrNever<ObjectType, {
39
+ ifNot: If<IsNever<KeysType>,
40
40
  ObjectType,
41
- _RequireOneOrNone<ObjectType, If<IsAny<KeysType>, keyof ObjectType, KeysType>>
42
- >>;
41
+ _RequireOneOrNone<ObjectType, If<IsAny<KeysType>, keyof ObjectType, KeysType>>>;
42
+ }>;
43
43
 
44
44
  type _RequireOneOrNone<ObjectType, KeysType extends keyof ObjectType> = (
45
45
  | RequireExactlyOne<ObjectType, KeysType>
@@ -3,12 +3,14 @@ import type {IsNever} from './is-never.d.ts';
3
3
  import type {Simplify} from './simplify.d.ts';
4
4
 
5
5
  /**
6
- Create a type from another type with all keys and nested keys set to required.
6
+ Create a deeply required version of another type.
7
7
 
8
8
  Use-cases:
9
9
  - Creating optional configuration interfaces where the underlying implementation still requires all options to be fully specified.
10
10
  - Modeling the resulting type after a deep merge with a set of defaults.
11
11
 
12
+ Use [`Required<T>`](https://www.typescriptlang.org/docs/handbook/utility-types.html#requiredtype) if you only need one level deep.
13
+
12
14
  @example
13
15
  ```
14
16
  import type {RequiredDeep} from 'type-fest';
@@ -1,5 +1,7 @@
1
1
  import type {ApplyDefaultOptions} from './internal/object.d.ts';
2
2
  import type {IfNotAnyOrNever, NonRecursiveType} from './internal/type.d.ts';
3
+ import type {IsAny} from './is-any.d.ts';
4
+ import type {IsUnknown} from './is-unknown.d.ts';
3
5
  import type {OptionalKeysOf} from './optional-keys-of.d.ts';
4
6
  import type {Simplify} from './simplify.d.ts';
5
7
  import type {UnknownArray} from './unknown-array.d.ts';
@@ -91,18 +93,24 @@ const userMaskSettings: UserMask = {
91
93
  @category Object
92
94
  */
93
95
  export type Schema<Type, Value, Options extends SchemaOptions = {}> =
94
- IfNotAnyOrNever<Type,
95
- _Schema<Type, Value, ApplyDefaultOptions<SchemaOptions, DefaultSchemaOptions, Options>>,
96
- Value, Value>;
96
+ IfNotAnyOrNever<Type, {
97
+ ifNot: _Schema<Type, Value, ApplyDefaultOptions<SchemaOptions, DefaultSchemaOptions, Options>>;
98
+ ifAny: Value;
99
+ ifNever: Value;
100
+ }>;
97
101
 
98
102
  type _Schema<Type, Value, Options extends Required<SchemaOptions>> =
99
- Type extends NonRecursiveType | Map<unknown, unknown> | Set<unknown> | ReadonlyMap<unknown, unknown> | ReadonlySet<unknown>
103
+ IsAny<Type> extends true
100
104
  ? Value
101
- : Type extends UnknownArray
102
- ? Options['recurseIntoArrays'] extends false
105
+ : IsUnknown<Type> extends true
106
+ ? Value
107
+ : Type extends NonRecursiveType | Map<unknown, unknown> | Set<unknown> | ReadonlyMap<unknown, unknown> | ReadonlySet<unknown>
103
108
  ? Value
104
- : SchemaHelper<Type, Value, Options>
105
- : SchemaHelper<Type, Value, Options>;
109
+ : Type extends UnknownArray
110
+ ? Options['recurseIntoArrays'] extends false
111
+ ? Value
112
+ : SchemaHelper<Type, Value, Options>
113
+ : SchemaHelper<Type, Value, Options>;
106
114
 
107
115
  /**
108
116
  Internal helper for {@link _Schema}.
@@ -14,6 +14,7 @@ import type {ScreamingSnakeCase} from 'type-fest';
14
14
 
15
15
  const someVariable: ScreamingSnakeCase<'fooBar'> = 'FOO_BAR';
16
16
  const someVariableNoSplitOnNumbers: ScreamingSnakeCase<'p2pNetwork', {splitOnNumbers: false}> = 'P2P_NETWORK';
17
+ const someVariableWithPunctuation: ScreamingSnakeCase<'div.card::after', {splitOnPunctuation: true}> = 'DIV_CARD_AFTER';
17
18
 
18
19
  ```
19
20
 
@@ -1,14 +1,17 @@
1
- import type {NonRecursiveType, StringToNumber} from './internal/index.d.ts';
1
+ import type {NonRecursiveType} from './internal/index.d.ts';
2
+ import type {IsAny} from './is-any.d.ts';
3
+ import type {NonNullableDeep} from './non-nullable-deep.d.ts';
2
4
  import type {Paths} from './paths.d.ts';
3
5
  import type {SetNonNullable} from './set-non-nullable.d.ts';
4
6
  import type {Simplify} from './simplify.d.ts';
7
+ import type {StringToNumber} from './string-to-number.d.ts';
5
8
  import type {UnionToTuple} from './union-to-tuple.d.ts';
6
9
  import type {UnknownArray} from './unknown-array.d.ts';
7
10
 
8
11
  /**
9
12
  Create a type that makes the specified keys non-nullable (removes `null` and `undefined`), supports deeply nested key paths, and leaves all other keys unchanged.
10
13
 
11
- NOTE: Optional modifiers (`?`) are not removed from properties. For example, `SetNonNullableDeep<{foo?: string | null | undefined}, 'foo'>` will result in `{foo?: string}`.
14
+ NOTE: Optional modifiers (`?`) are not removed from properties. For example, `SetNonNullableDeep<{foo?: string | null | undefined}, 'foo'>` will result in `{foo?: string}`. To remove both optional modifiers and nullables, use {@link SetRequiredDeep} in conjunction with this type.
12
15
 
13
16
  @example
14
17
  ```
@@ -55,8 +58,9 @@ type ArrayExample2 = SetNonNullableDeep<{a: [(number | null)?, (number | null)?]
55
58
 
56
59
  @category Object
57
60
  */
58
- export type SetNonNullableDeep<BaseType, KeyPaths extends Paths<BaseType>> =
59
- SetNonNullableDeepHelper<BaseType, UnionToTuple<KeyPaths>>;
61
+ export type SetNonNullableDeep<BaseType, KeyPaths extends Paths<BaseType>> = IsAny<KeyPaths> extends true
62
+ ? NonNullableDeep<BaseType>
63
+ : SetNonNullableDeepHelper<BaseType, UnionToTuple<KeyPaths>>;
60
64
 
61
65
  /**
62
66
  Internal helper for {@link SetNonNullableDeep}.
@@ -1,5 +1,5 @@
1
1
  /**
2
- Create a type that makes the given keys non-nullable, where the remaining keys are kept as is.
2
+ Create a type that makes the given keys non-nullable, while keeping the remaining keys as is.
3
3
 
4
4
  If no keys are given, all keys will be made non-nullable.
5
5
 
@@ -3,7 +3,7 @@ import type {HomomorphicPick} from './internal/index.d.ts';
3
3
  import type {Simplify} from './simplify.d.ts';
4
4
 
5
5
  /**
6
- Create a type that makes the given keys optional. The remaining keys are kept as is. The sister of the `SetRequired` type.
6
+ Create a type that makes the given keys optional, while keeping the remaining keys as is.
7
7
 
8
8
  Use-case: You want to define a single model where the only thing that changes is whether or not some of the keys are optional.
9
9
 
@@ -32,10 +32,10 @@ export type SetOptional<BaseType, Keys extends keyof BaseType> =
32
32
  type _SetOptional<BaseType, Keys extends keyof BaseType> =
33
33
  BaseType extends unknown // To distribute `BaseType` when it's a union type.
34
34
  ? Simplify<
35
- // Pick just the keys that are readonly from the base type.
36
- Except<BaseType, Keys> &
37
- // Pick the keys that should be mutable from the base type and make them mutable.
38
- Partial<HomomorphicPick<BaseType, Keys>>
35
+ // Pick just the keys that are readonly from the base type.
36
+ Except<BaseType, Keys>
37
+ // Pick the keys that should be mutable from the base type and make them mutable.
38
+ & Partial<HomomorphicPick<BaseType, Keys>>
39
39
  >
40
40
  : never;
41
41
 
@@ -3,7 +3,7 @@ import type {HomomorphicPick} from './internal/index.d.ts';
3
3
  import type {Simplify} from './simplify.d.ts';
4
4
 
5
5
  /**
6
- Create a type that makes the given keys readonly. The remaining keys are kept as is.
6
+ Create a type that makes the given keys readonly, while keeping the remaining keys as is.
7
7
 
8
8
  Use-case: You want to define a single model where the only thing that changes is whether or not some of the keys are readonly.
9
9
 
@@ -32,8 +32,8 @@ export type SetReadonly<BaseType, Keys extends keyof BaseType> =
32
32
  export type _SetReadonly<BaseType, Keys extends keyof BaseType> =
33
33
  BaseType extends unknown // To distribute `BaseType` when it's a union type.
34
34
  ? Simplify<
35
- Except<BaseType, Keys> &
36
- Readonly<HomomorphicPick<BaseType, Keys>>
35
+ Except<BaseType, Keys>
36
+ & Readonly<HomomorphicPick<BaseType, Keys>>
37
37
  >
38
38
  : never;
39
39
 
@@ -1,14 +1,15 @@
1
1
  import type {IsAny} from './is-any.d.ts';
2
- import type {NonRecursiveType, StringToNumber} from './internal/index.d.ts';
2
+ import type {NonRecursiveType} from './internal/index.d.ts';
3
3
  import type {Paths} from './paths.d.ts';
4
4
  import type {SetRequired} from './set-required.d.ts';
5
5
  import type {SimplifyDeep} from './simplify-deep.d.ts';
6
6
  import type {UnionToTuple} from './union-to-tuple.d.ts';
7
7
  import type {RequiredDeep} from './required-deep.d.ts';
8
8
  import type {UnknownArray} from './unknown-array.d.ts';
9
+ import type {StringToNumber} from './string-to-number.d.ts';
9
10
 
10
11
  /**
11
- Create a type that makes the given keys required. You can specify deeply nested key paths. The remaining keys are kept as is.
12
+ Create a type that makes the given keys required, with support for deeply nested key paths, while keeping the remaining keys as is.
12
13
 
13
14
  Use-case: Selectively make nested properties required in complex types like models.
14
15
 
@@ -1,12 +1,11 @@
1
1
  import type {Except} from './except.d.ts';
2
2
  import type {If} from './if.d.ts';
3
3
  import type {HomomorphicPick, IsArrayReadonly} from './internal/index.d.ts';
4
- import type {OptionalKeysOf} from './optional-keys-of.d.ts';
5
4
  import type {Simplify} from './simplify.d.ts';
6
5
  import type {UnknownArray} from './unknown-array.d.ts';
7
6
 
8
7
  /**
9
- Create a type that makes the given keys required. The remaining keys are kept as is. The sister of the `SetOptional` type.
8
+ Create a type that makes the given keys required, while keeping the remaining keys as is.
10
9
 
11
10
  Use-case: You want to define a single model where the only thing that changes is whether or not some of the keys are required.
12
11
 
@@ -43,9 +42,9 @@ type _SetRequired<BaseType, Keys extends keyof BaseType> =
43
42
  : never
44
43
  : Simplify<
45
44
  // Pick just the keys that are optional from the base type.
46
- Except<BaseType, Keys> &
45
+ Except<BaseType, Keys>
47
46
  // Pick the keys that should be required from the base type and make them required.
48
- Required<HomomorphicPick<BaseType, Keys>>
47
+ & Required<HomomorphicPick<BaseType, Keys>>
49
48
  >;
50
49
 
51
50
  /**
@@ -57,19 +56,21 @@ type SetArrayRequired<
57
56
  Counter extends any[] = [],
58
57
  Accumulator extends UnknownArray = [],
59
58
  > = TArray extends unknown // For distributing `TArray` when it's a union
60
- ? keyof TArray & `${number}` extends never
61
- // Exit if `TArray` is empty (e.g., []), or
62
- // `TArray` contains no non-rest elements preceding the rest element (e.g., `[...string[]]` or `[...string[], string]`).
63
- ? [...Accumulator, ...TArray]
64
- : TArray extends readonly [(infer First)?, ...infer Rest]
65
- ? '0' extends OptionalKeysOf<TArray> // If the first element of `TArray` is optional
66
- ? `${Counter['length']}` extends `${Keys & (string | number)}` // If the current index needs to be required
59
+ ? '0' extends keyof TArray
60
+ ? TArray extends readonly [(infer First)?, ...infer Rest]
61
+ // A tuple allows fewer than one element only when its first element is optional. Written in exactly this shape because equivalent ones, such as `[] extends TArray`, are several times slower.
62
+ // Unlike the other array loops, this one has no `any` guard above it, so the check is wrapped in tuples to stop `any` from matching both branches.
63
+ ? [TArray] extends [readonly [unknown, ...unknown[]]]
64
+ ? SetArrayRequired<Rest, Keys, [...Counter, any], [...Accumulator, First]>
65
+ : `${Counter['length']}` extends `${Keys & (string | number)}` // If the current index needs to be required
67
66
  ? SetArrayRequired<Rest, Keys, [...Counter, any], [...Accumulator, First]>
68
67
  // If the current element is optional, but it doesn't need to be required,
69
68
  // then we can exit early, since no further elements can now be made required.
70
69
  : [...Accumulator, ...TArray]
71
- : SetArrayRequired<Rest, Keys, [...Counter, any], [...Accumulator, TArray[0]]>
72
70
  : never // Should never happen, since `[(infer F)?, ...infer R]` is a top-type for arrays.
71
+ // Exit if `TArray` is empty (e.g., []), or
72
+ // `TArray` contains no non-rest elements preceding the rest element (e.g., `[...string[]]` or `[...string[], string]`).
73
+ : [...Accumulator, ...TArray]
73
74
  : never; // Should never happen
74
75
 
75
76
  export {};
@@ -1,4 +1,5 @@
1
- import type {NonRecursiveType, UnionMin, UnionMax, TupleLength, StaticPartOfArray, VariablePartOfArray, IsArrayReadonly, SetArrayAccess, ApplyDefaultOptions} from './internal/index.d.ts';
1
+ import type {NonRecursiveType, UnionMin, TupleLength, StaticPartOfArray, VariablePartOfArray, IsArrayReadonly, SetArrayAccess, ApplyDefaultOptions} from './internal/index.d.ts';
2
+ import type {UnionMax} from './union-max.d.ts';
2
3
  import type {IsNever} from './is-never.d.ts';
3
4
  import type {UnknownArray} from './unknown-array.d.ts';
4
5
 
@@ -106,11 +107,12 @@ export type SharedUnionFieldsDeep<Union, Options extends SharedUnionFieldsDeepOp
106
107
  Same as `SharedUnionFieldsDeep`, but accepts only `object`s and as inputs. Internal helper for `SharedUnionFieldsDeep`.
107
108
  */
108
109
  type SharedObjectUnionFieldsDeep<Union, Options extends Required<SharedUnionFieldsDeepOptions>> =
109
- // `keyof Union` can extract the same key in union type, if there is no same key, return never.
110
- keyof Union extends infer Keys
110
+ // Extract the shared keys from the union. Throughout this type, use `Keys` instead of `keyof Union`
111
+ // to prevent the mapped type from distributing over the union.
112
+ keyof Union extends infer Keys extends keyof Union
111
113
  ? IsNever<Keys> extends false
112
114
  ? {
113
- [Key in keyof Union]:
115
+ [Key in Keys]:
114
116
  Union[Key] extends NonRecursiveType
115
117
  ? Union[Key]
116
118
  // Remove `undefined` from the union to support optional
@@ -66,14 +66,14 @@ function displayPetInfoWithSharedUnionFields(petInfo: SharedUnionFields<Cat | Do
66
66
  @category Union
67
67
  */
68
68
  export type SharedUnionFields<Union> =
69
- Extract<Union, NonRecursiveType | ReadonlyMap<unknown, unknown> | ReadonlySet<unknown> | UnknownArray> extends infer SkippedMembers
70
- ? Exclude<Union, SkippedMembers> extends infer RelevantMembers
71
- ?
72
- | SkippedMembers
73
- | (IsNever<RelevantMembers> extends true
74
- ? never
75
- : Simplify<Pick<RelevantMembers, keyof RelevantMembers>>)
76
- : never
77
- : never;
69
+ Extract<Union, NonRecursiveType | ReadonlyMap<unknown, unknown> | ReadonlySet<unknown> | UnknownArray> extends infer SkippedMembers
70
+ ? Exclude<Union, SkippedMembers> extends infer RelevantMembers
71
+ ? // eslint-disable-line @stylistic/operator-linebreak
72
+ | SkippedMembers
73
+ | (IsNever<RelevantMembers> extends true
74
+ ? never
75
+ : Simplify<Pick<RelevantMembers, keyof RelevantMembers>>)
76
+ : never
77
+ : never;
78
78
 
79
79
  export {};
@@ -16,6 +16,7 @@ import type {SnakeCase} from 'type-fest';
16
16
  const someVariable: SnakeCase<'fooBar'> = 'foo_bar';
17
17
  const noSplitOnNumbers: SnakeCase<'p2pNetwork'> = 'p2p_network';
18
18
  const splitOnNumbers: SnakeCase<'p2pNetwork', {splitOnNumbers: true}> = 'p_2_p_network';
19
+ const splitOnPunctuation: SnakeCase<'div.card::after', {splitOnPunctuation: true}> = 'div_card_after';
19
20
 
20
21
  // Advanced
21
22
 
@@ -51,6 +51,13 @@ const splitOnNumbers: SnakeCasedPropertiesDeep<{line1: {line2: [{line3: string}]
51
51
  ],
52
52
  },
53
53
  };
54
+
55
+ const splitOnPunctuation: SnakeCasedPropertiesDeep<{'user@info': {'user::id': number; 'user::name': string}}, {splitOnPunctuation: true}> = {
56
+ 'user_info': {
57
+ 'user_id': 1,
58
+ 'user_name': 'Tom',
59
+ },
60
+ };
54
61
  ```
55
62
 
56
63
  @category Change case
@@ -4,7 +4,7 @@ import type {ApplyDefaultOptions} from './internal/index.d.ts';
4
4
  import type {WordsOptions} from './words.d.ts';
5
5
 
6
6
  /**
7
- Convert object properties to snake case but not recursively.
7
+ Convert top-level object properties to snake case.
8
8
 
9
9
  This can be useful when, for example, converting some API types from a different style.
10
10
 
@@ -28,6 +28,10 @@ const result: SnakeCasedProperties<User> = {
28
28
  const splitOnNumbers: SnakeCasedProperties<{line1: string}, {splitOnNumbers: true}> = {
29
29
  'line_1': 'string',
30
30
  };
31
+
32
+ const splitOnPunctuation: SnakeCasedProperties<{'foo::bar': string}, {splitOnPunctuation: true}> = {
33
+ 'foo_bar': 'string',
34
+ };
31
35
  ```
32
36
 
33
37
  @category Change case
@@ -95,9 +95,14 @@ type B = SomeExtend<[1?, 2?, '3'?], string | undefined>;
95
95
  @category Array
96
96
  */
97
97
  export type SomeExtend<TArray extends UnknownArray, Type, Options extends SomeExtendOptions = {}> =
98
- _SomeExtend<CollapseRestElement<TArray>, Type, ApplyDefaultOptions<SomeExtendOptions, DefaultSomeExtendOptions, Options>>;
99
-
100
- type _SomeExtend<TArray extends UnknownArray, Type, Options extends Required<SomeExtendOptions>> = IfNotAnyOrNever<TArray,
98
+ IfNotAnyOrNever<CollapseRestElement<TArray>, {
99
+ ifNot: _SomeExtend<CollapseRestElement<TArray>, Type, ApplyDefaultOptions<SomeExtendOptions, DefaultSomeExtendOptions, Options>>;
100
+ ifAny: false;
101
+ ifNever: false;
102
+ }>;
103
+
104
+ // Kept free of `IfNotAnyOrNever` so that it stays tail-recursive, which allows it to handle long arrays.
105
+ type _SomeExtend<TArray extends UnknownArray, Type, Options extends Required<SomeExtendOptions>> =
101
106
  TArray extends readonly [infer First, ...infer Rest]
102
107
  ? IsNever<First> extends true
103
108
  ? Or<Or<IsNever<Type>, IsAny<Type>>, Not<Options['strictNever']>> extends true
@@ -107,7 +112,6 @@ type _SomeExtend<TArray extends UnknownArray, Type, Options extends Required<Som
107
112
  : First extends Type
108
113
  ? true
109
114
  : _SomeExtend<Rest, Type, Options>
110
- : false,
111
- false, false>;
115
+ : false;
112
116
 
113
117
  export {};
@@ -1,6 +1,5 @@
1
1
  import type {IfNotAnyOrNever, IsExactOptionalPropertyTypesEnabled} from './internal/type.d.ts';
2
2
  import type {ApplyDefaultOptions} from './internal/object.d.ts';
3
- import type {IsOptionalKeyOf} from './is-optional-key-of.d.ts';
4
3
  import type {IsArrayReadonly} from './internal/array.d.ts';
5
4
  import type {UnknownArray} from './unknown-array.d.ts';
6
5
  import type {If} from './if.d.ts';
@@ -65,10 +64,12 @@ export type SplitOnRestElement<
65
64
  Options extends SplitOnRestElementOptions = {},
66
65
  > =
67
66
  Array_ extends unknown // For distributing `Array_`
68
- ? IfNotAnyOrNever<Array_, _SplitOnRestElement<
69
- Array_,
70
- ApplyDefaultOptions<SplitOnRestElementOptions, DefaultSplitOnRestElementOptions, Options>
71
- >> extends infer Result extends UnknownArray
67
+ ? IfNotAnyOrNever<Array_, {
68
+ ifNot: _SplitOnRestElement<
69
+ Array_,
70
+ ApplyDefaultOptions<SplitOnRestElementOptions, DefaultSplitOnRestElementOptions, Options>
71
+ >;
72
+ }> extends infer Result extends UnknownArray
72
73
  ? If<IsArrayReadonly<Array_>, Readonly<Result>, Result>
73
74
  : never // Should never happen
74
75
  : never; // Should never happen
@@ -82,25 +83,27 @@ export type _SplitOnRestElement<
82
83
  HeadAcc extends UnknownArray = [],
83
84
  TailAcc extends UnknownArray = [],
84
85
  > =
85
- keyof Array_ & `${number}` extends never
86
- // Enters this branch, if `Array_` is empty (e.g., []),
87
- // or `Array_` contains no non-rest elements preceding the rest element (e.g., `[...string[]]` or `[...string[], string]`).
88
- ? Array_ extends readonly [...infer Rest, infer Last]
89
- ? _SplitOnRestElement<Rest, Options, HeadAcc, [Last, ...TailAcc]> // Accumulate elements that are present after the rest element.
90
- : [HeadAcc, Array_ extends readonly [] ? [] : Array_, TailAcc] // Add the rest element between the accumulated elements.
91
- : Array_ extends readonly [(infer First)?, ...infer Rest]
86
+ '0' extends keyof Array_
87
+ // Enters this branch, if `Array_` starts with a non-rest element (e.g., `[string, number]` or `[string, ...number[]]`).
88
+ ? Array_ extends readonly [(infer First)?, ...infer Rest]
92
89
  ? _SplitOnRestElement<
93
90
  Rest, Options,
94
91
  [
95
92
  ...HeadAcc,
96
- ...IsOptionalKeyOf<Array_, '0'> extends true
97
- ? Options['preserveOptionalModifier'] extends false
93
+ // A tuple allows fewer than one element only when its first element is optional. Written in exactly this shape because equivalent ones, such as `[] extends Array_`, are several times slower.
94
+ ...Array_ extends readonly [unknown, ...unknown[]]
95
+ ? [First]
96
+ : Options['preserveOptionalModifier'] extends false
98
97
  ? [If<IsExactOptionalPropertyTypesEnabled, First, First | undefined>] // Add `| undefined` for optional elements, if `exactOptionalPropertyTypes` is disabled.
99
- : [First?]
100
- : [First],
98
+ : [First?],
101
99
  ],
102
100
  TailAcc
103
101
  > // Accumulate elements that are present before the rest element.
104
- : never; // Should never happen, since `[(infer First)?, ...infer Rest]` is a top-type for arrays.
102
+ : never // Should never happen, since `[(infer First)?, ...infer Rest]` is a top-type for arrays.
103
+ // Enters this branch, if `Array_` is empty (e.g., []),
104
+ // or `Array_` contains no non-rest elements preceding the rest element (e.g., `[...string[]]` or `[...string[], string]`).
105
+ : Array_ extends readonly [...infer Rest, infer Last]
106
+ ? _SplitOnRestElement<Rest, Options, HeadAcc, [Last, ...TailAcc]> // Accumulate elements that are present after the rest element.
107
+ : [HeadAcc, Array_ extends readonly [] ? [] : Array_, TailAcc]; // Add the rest element between the accumulated elements.
105
108
 
106
109
  export {};
package/source/split.d.ts CHANGED
@@ -1,6 +1,6 @@
1
1
  import type {And} from './and.d.ts';
2
2
  import type {ApplyDefaultOptions, Not} from './internal/index.d.ts';
3
- import type {IsStringLiteral} from './is-literal.d.ts';
3
+ import type {IsStringLiteral} from './is-string-literal.d.ts';
4
4
  import type {Or} from './or.d.ts';
5
5
 
6
6
  /**
@@ -64,27 +64,38 @@ export type Split<
64
64
  Delimiter extends string,
65
65
  Options extends SplitOptions = {},
66
66
  > =
67
- SplitHelper<S, Delimiter, ApplyDefaultOptions<SplitOptions, DefaultSplitOptions, Options>>;
67
+ _Split<S, Delimiter, ApplyDefaultOptions<SplitOptions, DefaultSplitOptions, Options>>;
68
68
 
69
- type SplitHelper<
69
+ type _Split<
70
70
  S extends string,
71
71
  Delimiter extends string,
72
72
  Options extends Required<SplitOptions>,
73
- Accumulator extends string[] = [],
74
73
  > = S extends string // For distributing `S`
75
74
  ? Delimiter extends string // For distributing `Delimiter`
76
75
  // If `strictLiteralChecks` is `false` OR `S` and `Delimiter` both are string literals, then perform the split
77
76
  ? Or<Not<Options['strictLiteralChecks']>, And<IsStringLiteral<S>, IsStringLiteral<Delimiter>>> extends true
78
- ? S extends `${infer Head}${Delimiter}${infer Tail}`
79
- ? SplitHelper<Tail, Delimiter, Options, [...Accumulator, Head]>
80
- : Delimiter extends ''
81
- ? S extends ''
82
- ? Accumulator
83
- : [...Accumulator, S]
84
- : [...Accumulator, S]
77
+ // The `extends infer` step keeps `SplitOnDelimiter` out of this conditional's tail-recursion chain, so the loop gets the full recursion budget to itself.
78
+ ? SplitOnDelimiter<S, Delimiter> extends infer Result ? Result : never
85
79
  // Otherwise, return `string[]`
86
80
  : string[]
87
81
  : never // Should never happen
88
82
  : never; // Should never happen
89
83
 
84
+ /**
85
+ Splits `S` on `Delimiter`.
86
+
87
+ `S` and `Delimiter` must already be distributed, so that the checks performed by `_Split` do not have to be repeated on every recursion step.
88
+ */
89
+ type SplitOnDelimiter<
90
+ S extends string,
91
+ Delimiter extends string,
92
+ Accumulator extends string[] = [],
93
+ > = S extends `${infer Head}${Delimiter}${infer Tail}`
94
+ ? SplitOnDelimiter<Tail, Delimiter, [...Accumulator, Head]>
95
+ : Delimiter extends ''
96
+ ? S extends ''
97
+ ? Accumulator
98
+ : [...Accumulator, S]
99
+ : [...Accumulator, S];
100
+
90
101
  export {};
@@ -0,0 +1,38 @@
1
+ import type {StringToArray} from './string-to-array.d.ts';
2
+
3
+ /**
4
+ Returns the length of the given string.
5
+
6
+ @example
7
+ ```
8
+ import type {StringLength} from 'type-fest';
9
+
10
+ type A = StringLength<'abcde'>;
11
+ //=> 5
12
+
13
+ type B = StringLength<'abcde' | 'fgh'>;
14
+ //=> 3 | 5
15
+ ```
16
+
17
+ For non-literal strings, the result is `number` because the length of a non-literal string can be any number.
18
+
19
+ @example
20
+ ```
21
+ import type {StringLength} from 'type-fest';
22
+
23
+ type A = StringLength<string>;
24
+ //=> number
25
+
26
+ type B = StringLength<Uppercase<string>>;
27
+ //=> number
28
+
29
+ type C = StringLength<`${string}abc`>;
30
+ //=> number
31
+ ```
32
+
33
+ @category String
34
+ @category Template literal
35
+ */
36
+ export type StringLength<S extends string> = StringToArray<S>['length'];
37
+
38
+ export {};
@@ -1,5 +1,6 @@
1
- import type {IsNumericLiteral} from './is-literal.d.ts';
1
+ import type {IsNumericLiteral} from './is-numeric-literal.d.ts';
2
2
  import type {IsNegative} from './numeric.d.ts';
3
+ import type {DigitCharacter} from './characters.d.ts';
3
4
 
4
5
  /**
5
6
  Returns a new string which contains the specified number of copies of a given string, just like `String#repeat()`.
@@ -9,9 +10,9 @@ Returns a new string which contains the specified number of copies of a given st
9
10
  import type {StringRepeat} from 'type-fest';
10
11
 
11
12
  declare function stringRepeat<
12
- Input extends string,
13
+ S extends string,
13
14
  Count extends number,
14
- >(input: Input, count: Count): StringRepeat<Input, Count>;
15
+ >(input: S, count: Count): StringRepeat<S, Count>;
15
16
 
16
17
  // The return type is the exact string literal, not just `string`.
17
18
 
@@ -22,28 +23,54 @@ stringRepeat('=', 3);
22
23
  //=> '==='
23
24
  ```
24
25
 
26
+ Note: If the specified count has a decimal part, the decimal part will be ignored.
27
+
28
+ @example
29
+ ```
30
+ import type {StringRepeat} from 'type-fest';
31
+
32
+ type DecimalCount = StringRepeat<'foo', 2.5>;
33
+ //=> 'foofoo'
34
+ ```
35
+
25
36
  @category String
26
37
  @category Template literal
27
38
  */
28
- export type StringRepeat<
29
- Input extends string,
30
- Count extends number,
31
- > = StringRepeatHelper<Input, Count>;
32
-
33
- type StringRepeatHelper<
34
- Input extends string,
35
- Count extends number,
36
- Counter extends never[] = [],
37
- Accumulator extends string = '',
38
- > =
39
- IsNegative<Count> extends true
40
- ? never
41
- : Input extends ''
42
- ? ''
43
- : Count extends Counter['length']
44
- ? Accumulator
39
+ export type StringRepeat<S extends string, Count extends number> =
40
+ Count extends unknown // To distribute `Count`
41
+ ? IsNegative<Count> extends true
42
+ ? never
43
+ : S extends ''
44
+ ? ''
45
45
  : IsNumericLiteral<Count> extends false
46
46
  ? string
47
- : StringRepeatHelper<Input, Count, [...Counter, never], `${Accumulator}${Input}`>;
47
+ : `${Count}` extends `${string}e${string}`
48
+ ? string
49
+ : BuildStringDigitByDigit<S, `${Count}`>
50
+ : never;
51
+
52
+ type BuildStringDigitByDigit<S extends string, Count extends string, Accumulator extends string = ''> =
53
+ Count extends `${infer First extends DigitCharacter}${infer Rest}`
54
+ ? BuildStringDigitByDigit<
55
+ S,
56
+ Rest,
57
+ `${RepeatStringTenTimes<Accumulator>}${DigitStringRepeat<S, First>}`
58
+ >
59
+ : Accumulator;
60
+
61
+ type RepeatStringTenTimes<S extends string> = `${S}${S}${S}${S}${S}${S}${S}${S}${S}${S}`;
62
+
63
+ type DigitStringRepeat<S extends string, Digit extends DigitCharacter> = [
64
+ '',
65
+ `${S}`,
66
+ `${S}${S}`,
67
+ `${S}${S}${S}`,
68
+ `${S}${S}${S}${S}`,
69
+ `${S}${S}${S}${S}${S}`,
70
+ `${S}${S}${S}${S}${S}${S}`,
71
+ `${S}${S}${S}${S}${S}${S}${S}`,
72
+ `${S}${S}${S}${S}${S}${S}${S}${S}`,
73
+ `${S}${S}${S}${S}${S}${S}${S}${S}${S}`,
74
+ ][Digit];
48
75
 
49
76
  export {};
@@ -1,6 +1,6 @@
1
1
  import type {Join} from './join.d.ts';
2
2
  import type {ArraySlice} from './array-slice.d.ts';
3
- import type {StringToArray} from './internal/index.d.ts';
3
+ import type {StringToArray} from './string-to-array.d.ts';
4
4
 
5
5
  /**
6
6
  Returns a string slice of a given range, just like `String#slice()`.