type-fest 4.35.0 → 4.37.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.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "type-fest",
3
- "version": "4.35.0",
3
+ "version": "4.37.0",
4
4
  "description": "A collection of essential TypeScript types",
5
5
  "license": "(MIT OR CC0-1.0)",
6
6
  "repository": "sindresorhus/type-fest",
@@ -91,7 +91,7 @@ type ArraySliceHelper<
91
91
  ? Sum<ArrayLength, Start> extends infer AddResult extends number
92
92
  ? number extends AddResult // (ArrayLength + Start) < 0
93
93
  ? 0
94
- : AddResult
94
+ : GreaterThan<AddResult, 0> extends true ? AddResult : 0
95
95
  : never
96
96
  : Start,
97
97
  PositiveE extends number = IsNegative<End> extends true ? Sum<ArrayLength, End> : End,
@@ -21,7 +21,9 @@ The implementation of `SplitArrayByIndex` for variable length arrays.
21
21
  type SplitVariableArrayByIndex<T extends UnknownArray,
22
22
  SplitIndex extends number,
23
23
  T1 = Subtract<SplitIndex, StaticPartOfArray<T>['length']>,
24
- T2 = T1 extends number ? BuildTuple<T1, VariablePartOfArray<T>[number]> : [],
24
+ T2 = T1 extends number
25
+ ? BuildTuple<GreaterThanOrEqual<T1, 0> extends true ? T1 : number, VariablePartOfArray<T>[number]>
26
+ : [],
25
27
  > =
26
28
  SplitIndex extends 0
27
29
  ? [[], T]
@@ -75,14 +75,9 @@ type CamelCasedPropertiesArrayDeep<Value extends UnknownArray> =
75
75
  : // Leading spread array
76
76
  Value extends readonly [...infer U, infer V]
77
77
  ? [...CamelCasedPropertiesDeep<U>, CamelCasedPropertiesDeep<V>]
78
- : Value extends readonly [...infer U, infer V]
79
- ? readonly [
80
- ...CamelCasedPropertiesDeep<U>,
81
- CamelCasedPropertiesDeep<V>,
82
- ]
83
- : // Array
84
- Value extends Array<infer U>
85
- ? Array<CamelCasedPropertiesDeep<U>>
86
- : Value extends ReadonlyArray<infer U>
87
- ? ReadonlyArray<CamelCasedPropertiesDeep<U>>
88
- : never;
78
+ : // Array
79
+ Value extends Array<infer U>
80
+ ? Array<CamelCasedPropertiesDeep<U>>
81
+ : Value extends ReadonlyArray<infer U>
82
+ ? ReadonlyArray<CamelCasedPropertiesDeep<U>>
83
+ : never;
@@ -1,54 +1,23 @@
1
- import type {UpperCaseCharacters, WordSeparators} from './internal';
2
-
3
- // Transforms a string that is fully uppercase into a fully lowercase version. Needed to add support for SCREAMING_SNAKE_CASE, see https://github.com/sindresorhus/type-fest/issues/385
4
- type UpperCaseToLowerCase<T extends string> = T extends Uppercase<T> ? Lowercase<T> : T;
5
-
6
- // This implementation does not support SCREAMING_SNAKE_CASE, it is used internally by `SplitIncludingDelimiters`.
7
- type SplitIncludingDelimiters_<Source extends string, Delimiter extends string> =
8
- Source extends '' ? [] :
9
- Source extends `${infer FirstPart}${Delimiter}${infer SecondPart}` ?
10
- (
11
- Source extends `${FirstPart}${infer UsedDelimiter}${SecondPart}`
12
- ? UsedDelimiter extends Delimiter
13
- ? Source extends `${infer FirstPart}${UsedDelimiter}${infer SecondPart}`
14
- ? [...SplitIncludingDelimiters<FirstPart, Delimiter>, UsedDelimiter, ...SplitIncludingDelimiters<SecondPart, Delimiter>]
15
- : never
16
- : never
17
- : never
18
- ) :
19
- [Source];
20
-
21
- /**
22
- Unlike a simpler split, this one includes the delimiter splitted on in the resulting array literal. This is to enable splitting on, for example, upper-case characters.
23
-
24
- @category Template literal
25
- */
26
- export type SplitIncludingDelimiters<Source extends string, Delimiter extends string> = SplitIncludingDelimiters_<UpperCaseToLowerCase<Source>, Delimiter>;
1
+ import type {IsStringLiteral} from './is-literal';
2
+ import type {Words, WordsOptions} from './words';
27
3
 
28
4
  /**
29
- Format a specific part of the splitted string literal that `StringArrayToDelimiterCase<>` fuses together, ensuring desired casing.
30
-
31
- @see StringArrayToDelimiterCase
5
+ Convert an array of words to delimiter case starting with a delimiter with input capitalization.
32
6
  */
33
- type StringPartToDelimiterCase<StringPart extends string, Start extends boolean, UsedWordSeparators extends string, UsedUpperCaseCharacters extends string, Delimiter extends string> =
34
- StringPart extends UsedWordSeparators ? Delimiter :
35
- Start extends true ? Lowercase<StringPart> :
36
- StringPart extends UsedUpperCaseCharacters ? `${Delimiter}${Lowercase<StringPart>}` :
37
- StringPart;
38
-
39
- /**
40
- Takes the result of a splitted string literal and recursively concatenates it together into the desired casing.
41
-
42
- It receives `UsedWordSeparators` and `UsedUpperCaseCharacters` as input to ensure it's fully encapsulated.
43
-
44
- @see SplitIncludingDelimiters
45
- */
46
- type StringArrayToDelimiterCase<Parts extends readonly any[], Start extends boolean, UsedWordSeparators extends string, UsedUpperCaseCharacters extends string, Delimiter extends string> =
47
- Parts extends [`${infer FirstPart}`, ...infer RemainingParts]
48
- ? `${StringPartToDelimiterCase<FirstPart, Start, UsedWordSeparators, UsedUpperCaseCharacters, Delimiter>}${StringArrayToDelimiterCase<RemainingParts, false, UsedWordSeparators, UsedUpperCaseCharacters, Delimiter>}`
49
- : Parts extends [string]
50
- ? string
51
- : '';
7
+ type DelimiterCaseFromArray<
8
+ Words extends string[],
9
+ Delimiter extends string,
10
+ OutputString extends string = '',
11
+ > = Words extends [
12
+ infer FirstWord extends string,
13
+ ...infer RemainingWords extends string[],
14
+ ]
15
+ ? DelimiterCaseFromArray<RemainingWords, Delimiter, `${OutputString}${Delimiter}${FirstWord}`>
16
+ : OutputString;
17
+
18
+ type RemoveFirstLetter<S extends string> = S extends `${infer _}${infer Rest}`
19
+ ? Rest
20
+ : '';
52
21
 
53
22
  /**
54
23
  Convert a string literal to a custom string delimiter casing.
@@ -65,6 +34,7 @@ import type {DelimiterCase} from 'type-fest';
65
34
  // Simple
66
35
 
67
36
  const someVariable: DelimiterCase<'fooBar', '#'> = 'foo#bar';
37
+ const someVariableNoSplitOnNumbers: DelimiterCase<'p2pNetwork', '#', {splitOnNumbers: false}> = 'p2p#network';
68
38
 
69
39
  // Advanced
70
40
 
@@ -87,13 +57,17 @@ const rawCliOptions: OddlyCasedProperties<SomeOptions> = {
87
57
 
88
58
  @category Change case
89
59
  @category Template literal
90
- */
91
- export type DelimiterCase<Value, Delimiter extends string> = string extends Value ? Value : Value extends string
92
- ? StringArrayToDelimiterCase<
93
- SplitIncludingDelimiters<Value, WordSeparators | UpperCaseCharacters>,
94
- true,
95
- WordSeparators,
96
- UpperCaseCharacters,
97
- Delimiter
98
- >
60
+ */
61
+ export type DelimiterCase<
62
+ Value,
63
+ Delimiter extends string,
64
+ Options extends WordsOptions = {splitOnNumbers: false},
65
+ > = Value extends string
66
+ ? IsStringLiteral<Value> extends false
67
+ ? Value
68
+ : Lowercase<
69
+ RemoveFirstLetter<
70
+ DelimiterCaseFromArray<Words<Value, Options>, Delimiter>
71
+ >
72
+ >
99
73
  : Value;
@@ -1,4 +1,5 @@
1
1
  import type {IsNever} from '../is-never';
2
+ import type {NegativeInfinity, PositiveInfinity} from '../numeric';
2
3
  import type {UnknownArray} from '../unknown-array';
3
4
  import type {StringToNumber} from './string';
4
5
 
@@ -89,3 +90,29 @@ type InternalUnionMax<N extends number, T extends UnknownArray = []> =
89
90
  : T['length'] extends N
90
91
  ? InternalUnionMax<Exclude<N, T['length']>, T>
91
92
  : InternalUnionMax<N, [...T, unknown]>;
93
+
94
+ /**
95
+ Returns the number with reversed sign.
96
+
97
+ @example
98
+ ```
99
+ ReverseSign<-1>;
100
+ //=> 1
101
+
102
+ ReverseSign<1>;
103
+ //=> -1
104
+
105
+ ReverseSign<NegativeInfinity>
106
+ //=> PositiveInfinity
107
+
108
+ ReverseSign<PositiveInfinity>
109
+ //=> NegativeInfinity
110
+ ```
111
+ */
112
+ export type ReverseSign<N extends number> =
113
+ // Handle edge cases
114
+ N extends 0 ? 0 : N extends PositiveInfinity ? NegativeInfinity : N extends NegativeInfinity ? PositiveInfinity :
115
+ // Handle negative numbers
116
+ `${N}` extends `-${infer P extends number}` ? P
117
+ // Handle positive numbers
118
+ : `-${N}` extends `${infer R extends number}` ? R : never;
@@ -1,4 +1,5 @@
1
1
  import type {DelimiterCase} from './delimiter-case';
2
+ import type {WordsOptions} from './words';
2
3
 
3
4
  /**
4
5
  Convert a string literal to kebab-case.
@@ -12,6 +13,7 @@ import type {KebabCase} from 'type-fest';
12
13
  // Simple
13
14
 
14
15
  const someVariable: KebabCase<'fooBar'> = 'foo-bar';
16
+ const someVariableNoSplitOnNumbers: KebabCase<'p2pNetwork', {splitOnNumbers: false}> = 'p2p-network';
15
17
 
16
18
  // Advanced
17
19
 
@@ -35,4 +37,7 @@ const rawCliOptions: KebabCasedProperties<CliOptions> = {
35
37
  @category Change case
36
38
  @category Template literal
37
39
  */
38
- export type KebabCase<Value> = DelimiterCase<Value, '-'>;
40
+ export type KebabCase<
41
+ Value,
42
+ Options extends WordsOptions = {splitOnNumbers: false},
43
+ > = DelimiterCase<Value, '-', Options>;
package/source/paths.d.ts CHANGED
@@ -246,13 +246,7 @@ type InternalPaths<T, Options extends Required<PathsOptions>> =
246
246
  bracketNotation: Options['bracketNotation'];
247
247
  maxRecursionDepth: Subtract<MaxDepth, 1>;
248
248
  leavesOnly: Options['leavesOnly'];
249
- depth: Options['depth'] extends infer Depth extends number // For distributing `Options['depth']`
250
- ? Depth extends 0 // Don't subtract further if `Depth` has reached `0`
251
- ? never
252
- : ToString<Depth> extends `-${number}` // Don't subtract if `Depth` is -ve
253
- ? never
254
- : Subtract<Options['depth'], 1> // If `Subtract` supported -ve numbers, then `depth` could have simply been `Subtract<Options['depth'], 1>`
255
- : never; // Should never happen
249
+ depth: Subtract<Options['depth'], 1>;
256
250
  }> extends infer SubPath
257
251
  ? SubPath extends string | number
258
252
  ? (
@@ -60,8 +60,20 @@ export type Replace<
60
60
  Search extends string,
61
61
  Replacement extends string,
62
62
  Options extends ReplaceOptions = {},
63
- > = Input extends `${infer Head}${Search}${infer Tail}`
64
- ? Options['all'] extends true
65
- ? `${Head}${Replacement}${Replace<Tail, Search, Replacement, Options>}`
66
- : `${Head}${Replacement}${Tail}`
67
- : Input;
63
+ > = _Replace<Input, Search, Replacement, Options>;
64
+
65
+ type _Replace<
66
+ Input extends string,
67
+ Search extends string,
68
+ Replacement extends string,
69
+ Options extends ReplaceOptions,
70
+ Accumulator extends string = '',
71
+ > = Search extends string // For distributing `Search`
72
+ ? Replacement extends string // For distributing `Replacement`
73
+ ? Input extends `${infer Head}${Search}${infer Tail}`
74
+ ? Options['all'] extends true
75
+ ? _Replace<Tail, Search, Replacement, Options, `${Accumulator}${Head}${Replacement}`>
76
+ : `${Head}${Replacement}${Tail}`
77
+ : `${Accumulator}${Input}`
78
+ : never
79
+ : never;
@@ -1,15 +1,5 @@
1
- import type {SplitIncludingDelimiters} from './delimiter-case';
2
1
  import type {SnakeCase} from './snake-case';
3
- import type {Includes} from './includes';
4
-
5
- /**
6
- Returns a boolean for whether the string is screaming snake case.
7
- */
8
- type IsScreamingSnakeCase<Value extends string> = Value extends Uppercase<Value>
9
- ? Includes<SplitIncludingDelimiters<Lowercase<Value>, '_'>, '_'> extends true
10
- ? true
11
- : false
12
- : false;
2
+ import type {WordsOptions} from './words';
13
3
 
14
4
  /**
15
5
  Convert a string literal to screaming-snake-case.
@@ -21,13 +11,14 @@ This can be useful when, for example, converting a camel-cased object property t
21
11
  import type {ScreamingSnakeCase} from 'type-fest';
22
12
 
23
13
  const someVariable: ScreamingSnakeCase<'fooBar'> = 'FOO_BAR';
14
+ const someVariableNoSplitOnNumbers: ScreamingSnakeCase<'p2pNetwork', {splitOnNumbers: false}> = 'P2P_NETWORK';
15
+
24
16
  ```
25
17
 
26
18
  @category Change case
27
19
  @category Template literal
28
- */
29
- export type ScreamingSnakeCase<Value> = Value extends string
30
- ? IsScreamingSnakeCase<Value> extends true
31
- ? Value
32
- : Uppercase<SnakeCase<Value>>
33
- : Value;
20
+ */
21
+ export type ScreamingSnakeCase<
22
+ Value,
23
+ Options extends WordsOptions = {splitOnNumbers: false},
24
+ > = Value extends string ? Uppercase<SnakeCase<Value, Options>> : Value;
@@ -1,4 +1,5 @@
1
1
  import type {DelimiterCase} from './delimiter-case';
2
+ import type {WordsOptions} from './words';
2
3
 
3
4
  /**
4
5
  Convert a string literal to snake-case.
@@ -12,6 +13,7 @@ import type {SnakeCase} from 'type-fest';
12
13
  // Simple
13
14
 
14
15
  const someVariable: SnakeCase<'fooBar'> = 'foo_bar';
16
+ const someVariableNoSplitOnNumbers: SnakeCase<'p2pNetwork', {splitOnNumbers: false}> = 'p2p_network';
15
17
 
16
18
  // Advanced
17
19
 
@@ -35,4 +37,7 @@ const dbResult: SnakeCasedProperties<ModelProps> = {
35
37
  @category Change case
36
38
  @category Template literal
37
39
  */
38
- export type SnakeCase<Value> = DelimiterCase<Value, '_'>;
40
+ export type SnakeCase<
41
+ Value,
42
+ Options extends WordsOptions = {splitOnNumbers: false},
43
+ > = DelimiterCase<Value, '_', Options>;
package/source/split.d.ts CHANGED
@@ -1,3 +1,43 @@
1
+ import type {And} from './and';
2
+ import type {Not} from './internal';
3
+ import type {IsStringLiteral} from './is-literal';
4
+ import type {Or} from './or';
5
+
6
+ /**
7
+ Split options.
8
+
9
+ @see {@link Split}
10
+ */
11
+ type SplitOptions = {
12
+ /**
13
+ 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.
14
+
15
+ 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.
16
+
17
+ @default false
18
+
19
+ @example
20
+ ```ts
21
+ type Example1 = Split<`foo.${string}.bar`, '.', {strictLiteralChecks: false}>;
22
+ //=> ['foo', string, 'bar']
23
+
24
+ type Example2 = Split<`foo.${string}`, '.', {strictLiteralChecks: true}>;
25
+ //=> string[]
26
+
27
+ type Example3 = Split<'foobarbaz', `b${string}`, {strictLiteralChecks: false}>;
28
+ //=> ['foo', 'r', 'z']
29
+
30
+ type Example4 = Split<'foobarbaz', `b${string}`, {strictLiteralChecks: true}>;
31
+ //=> string[]
32
+ ```
33
+ */
34
+ strictLiteralChecks?: boolean;
35
+ };
36
+
37
+ type DefaultSplitOptions = {
38
+ strictLiteralChecks: false;
39
+ };
40
+
1
41
  /**
2
42
  Represents an array of strings split using a given character or character set.
3
43
 
@@ -16,20 +56,34 @@ let array: Item[];
16
56
  array = split(items, ',');
17
57
  ```
18
58
 
59
+ @see {@link SplitOptions}
60
+
19
61
  @category String
20
62
  @category Template literal
21
63
  */
22
64
  export type Split<
23
65
  S extends string,
24
66
  Delimiter extends string,
25
- > = SplitHelper<S, Delimiter>;
67
+ Options extends SplitOptions = {},
68
+ > = SplitHelper<S, Delimiter, {
69
+ strictLiteralChecks: Options['strictLiteralChecks'] extends boolean ? Options['strictLiteralChecks'] : DefaultSplitOptions['strictLiteralChecks'];
70
+ }>;
26
71
 
27
72
  type SplitHelper<
28
73
  S extends string,
29
74
  Delimiter extends string,
75
+ Options extends Required<SplitOptions>,
30
76
  Accumulator extends string[] = [],
31
- > = S extends `${infer Head}${Delimiter}${infer Tail}`
32
- ? SplitHelper<Tail, Delimiter, [...Accumulator, Head]>
33
- : Delimiter extends ''
34
- ? Accumulator
35
- : [...Accumulator, S];
77
+ > = S extends string // For distributing `S`
78
+ ? Delimiter extends string // For distributing `Delimeter`
79
+ // If `strictLiteralChecks` is `false` OR `S` and `Delimiter` both are string literals, then perform the split
80
+ ? Or<Not<Options['strictLiteralChecks']>, And<IsStringLiteral<S>, IsStringLiteral<Delimiter>>> extends true
81
+ ? S extends `${infer Head}${Delimiter}${infer Tail}`
82
+ ? SplitHelper<Tail, Delimiter, Options, [...Accumulator, Head]>
83
+ : Delimiter extends ''
84
+ ? Accumulator
85
+ : [...Accumulator, S]
86
+ // Otherwise, return `string[]`
87
+ : string[]
88
+ : never // Should never happen
89
+ : never; // Should never happen
@@ -1,17 +1,12 @@
1
- import type {NumberAbsolute, BuildTuple} from './internal';
2
- import type {IsEqual} from './is-equal';
1
+ import type {NumberAbsolute, BuildTuple, ReverseSign} from './internal';
3
2
  import type {PositiveInfinity, NegativeInfinity, IsNegative} from './numeric';
4
3
  import type {LessThan} from './less-than';
5
- import type {Sum} from './sum';
6
- import type {And} from './and';
7
- import type {Or} from './or';
8
4
 
9
5
  /**
10
6
  Returns the difference between two numbers.
11
7
 
12
8
  Note:
13
9
  - A or B can only support `-999` ~ `999`.
14
- - If the result is negative, you can only get `number`.
15
10
 
16
11
  @example
17
12
  ```
@@ -24,7 +19,10 @@ Subtract<111, -222>;
24
19
  //=> 333
25
20
 
26
21
  Subtract<-111, 222>;
27
- //=> number
22
+ //=> -333
23
+
24
+ Subtract<18, 96>;
25
+ //=> -78
28
26
 
29
27
  Subtract<PositiveInfinity, 9999>;
30
28
  //=> PositiveInfinity
@@ -35,36 +33,51 @@ Subtract<PositiveInfinity, PositiveInfinity>;
35
33
 
36
34
  @category Numeric
37
35
  */
38
- // TODO: Support big integer and negative number.
39
- export type Subtract<A extends number, B extends number> = number extends A | B
40
- ? number
41
- : [
42
- IsEqual<A, PositiveInfinity>, IsEqual<A, NegativeInfinity>,
43
- IsEqual<B, PositiveInfinity>, IsEqual<B, NegativeInfinity>,
44
- ] extends infer R extends [boolean, boolean, boolean, boolean]
45
- ? Or<
46
- And<IsEqual<R[0], true>, IsEqual<R[2], false>>,
47
- And<IsEqual<R[3], true>, IsEqual<R[1], false>>
48
- > extends true
49
- ? PositiveInfinity
50
- : Or<
51
- And<IsEqual<R[1], true>, IsEqual<R[3], false>>,
52
- And<IsEqual<R[2], true>, IsEqual<R[0], false>>
53
- > extends true
54
- ? NegativeInfinity
55
- : true extends R[number]
56
- ? number
57
- : [IsNegative<A>, IsNegative<B>] extends infer R
58
- ? [false, false] extends R
59
- ? BuildTuple<A> extends infer R
60
- ? R extends [...BuildTuple<B>, ...infer R]
61
- ? R['length']
62
- : number
63
- : never
64
- : LessThan<A, B> extends true
65
- ? number
66
- : [false, true] extends R
67
- ? Sum<A, NumberAbsolute<B>>
68
- : Subtract<NumberAbsolute<B>, NumberAbsolute<A>>
69
- : never
36
+ // TODO: Support big integer.
37
+ export type Subtract<A extends number, B extends number> =
38
+ // Handle cases when A or B is the actual "number" type
39
+ number extends A | B ? number
40
+ // Handle cases when A and B are both +/- infinity
41
+ : A extends B & (PositiveInfinity | NegativeInfinity) ? number
42
+ // Handle cases when A is - infinity or B is + infinity
43
+ : A extends NegativeInfinity ? NegativeInfinity : B extends PositiveInfinity ? NegativeInfinity
44
+ // Handle cases when A is + infinity or B is - infinity
45
+ : A extends PositiveInfinity ? PositiveInfinity : B extends NegativeInfinity ? PositiveInfinity
46
+ // Handle case when numbers are equal to each other
47
+ : A extends B ? 0
48
+ // Handle cases when A or B is 0
49
+ : A extends 0 ? ReverseSign<B> : B extends 0 ? A
50
+ // Handle remaining regular cases
51
+ : SubtractPostChecks<A, B>;
52
+
53
+ /**
54
+ Subtracts two numbers A and B, such that they are not equal and neither of them are 0, +/- infinity or the `number` type
55
+ */
56
+ type SubtractPostChecks<A extends number, B extends number, AreNegative = [IsNegative<A>, IsNegative<B>]> =
57
+ AreNegative extends [false, false]
58
+ ? SubtractPositives<A, B>
59
+ : AreNegative extends [true, true]
60
+ // When both numbers are negative we subtract the absolute values and then reverse the sign
61
+ ? ReverseSign<SubtractPositives<NumberAbsolute<A>, NumberAbsolute<B>>>
62
+ // When the signs are different we can add the absolute values and then reverse the sign if A < B
63
+ : [...BuildTuple<NumberAbsolute<A>>, ...BuildTuple<NumberAbsolute<B>>] extends infer R extends unknown[]
64
+ ? LessThan<A, B> extends true ? ReverseSign<R['length']> : R['length']
65
+ : never;
66
+
67
+ /**
68
+ Subtracts two positive numbers.
69
+ */
70
+ type SubtractPositives<A extends number, B extends number> =
71
+ LessThan<A, B> extends true
72
+ // When A < B we can reverse the result of B - A
73
+ ? ReverseSign<SubtractIfAGreaterThanB<B, A>>
74
+ : SubtractIfAGreaterThanB<A, B>;
75
+
76
+ /**
77
+ Subtracts two positive numbers A and B such that A > B.
78
+ */
79
+ type SubtractIfAGreaterThanB<A extends number, B extends number> =
80
+ // This is where we always want to end up and do the actual subtraction
81
+ BuildTuple<A> extends [...BuildTuple<B>, ...infer R]
82
+ ? R['length']
70
83
  : never;
package/source/sum.d.ts CHANGED
@@ -1,17 +1,12 @@
1
- import type {NumberAbsolute, BuildTuple, TupleMax, TupleMin} from './internal';
2
- import type {IsEqual} from './is-equal';
1
+ import type {NumberAbsolute, BuildTuple, TupleMax, ReverseSign} from './internal';
3
2
  import type {PositiveInfinity, NegativeInfinity, IsNegative} from './numeric';
4
3
  import type {Subtract} from './subtract';
5
- import type {And} from './and';
6
- import type {Or} from './or';
7
4
 
8
5
  /**
9
6
  Returns the sum of two numbers.
10
7
 
11
8
  Note:
12
9
  - A or B can only support `-999` ~ `999`.
13
- - A and B can only be small integers, less than 1000.
14
- - If the result is negative, you can only get `number`.
15
10
 
16
11
  @example
17
12
  ```
@@ -24,7 +19,7 @@ Sum<-111, 222>;
24
19
  //=> 111
25
20
 
26
21
  Sum<111, -222>;
27
- //=> number
22
+ //=> -111
28
23
 
29
24
  Sum<PositiveInfinity, -9999>;
30
25
  //=> PositiveInfinity
@@ -35,36 +30,49 @@ Sum<PositiveInfinity, NegativeInfinity>;
35
30
 
36
31
  @category Numeric
37
32
  */
38
- // TODO: Support big integer and negative number.
39
- export type Sum<A extends number, B extends number> = number extends A | B
40
- ? number
41
- : [
42
- IsEqual<A, PositiveInfinity>, IsEqual<A, NegativeInfinity>,
43
- IsEqual<B, PositiveInfinity>, IsEqual<B, NegativeInfinity>,
44
- ] extends infer R extends [boolean, boolean, boolean, boolean]
45
- ? Or<
46
- And<IsEqual<R[0], true>, IsEqual<R[3], false>>,
47
- And<IsEqual<R[2], true>, IsEqual<R[1], false>>
48
- > extends true
49
- ? PositiveInfinity
50
- : Or<
51
- And<IsEqual<R[1], true>, IsEqual<R[2], false>>,
52
- And<IsEqual<R[3], true>, IsEqual<R[0], false>>
53
- > extends true
54
- ? NegativeInfinity
55
- : true extends R[number]
56
- ? number
57
- : ([IsNegative<A>, IsNegative<B>] extends infer R
58
- ? [false, false] extends R
59
- ? [...BuildTuple<A>, ...BuildTuple<B>]['length']
60
- : [true, true] extends R
61
- ? number
62
- : TupleMax<[NumberAbsolute<A>, NumberAbsolute<B>]> extends infer Max_
63
- ? TupleMin<[NumberAbsolute<A>, NumberAbsolute<B>]> extends infer Min_ extends number
64
- ? Max_ extends A | B
65
- ? Subtract<Max_, Min_>
66
- : number
67
- : never
68
- : never
69
- : never) & number
33
+ // TODO: Support big integer.
34
+ export type Sum<A extends number, B extends number> =
35
+ // Handle cases when A or B is the actual "number" type
36
+ number extends A | B ? number
37
+ // Handle cases when A and B are both +/- infinity
38
+ : A extends B & (PositiveInfinity | NegativeInfinity) ? A // A or B could be used here as they are equal
39
+ // Handle cases when A and B are opposite infinities
40
+ : A | B extends PositiveInfinity | NegativeInfinity ? number
41
+ // Handle cases when A is +/- infinity
42
+ : A extends PositiveInfinity | NegativeInfinity ? A
43
+ // Handle cases when B is +/- infinity
44
+ : B extends PositiveInfinity | NegativeInfinity ? B
45
+ // Handle cases when A or B is 0 or it's the same number with different signs
46
+ : A extends 0 ? B : B extends 0 ? A : A extends ReverseSign<B> ? 0
47
+ // Handle remaining regular cases
48
+ : SumPostChecks<A, B>;
49
+
50
+ /**
51
+ Adds two numbers A and B, such that they are not equal with different signs and neither of them are 0, +/- infinity or the `number` type
52
+ */
53
+ type SumPostChecks<A extends number, B extends number, AreNegative = [IsNegative<A>, IsNegative<B>]> =
54
+ AreNegative extends [false, false]
55
+ // When both numbers are positive we can add them together
56
+ ? SumPositives<A, B>
57
+ : AreNegative extends [true, true]
58
+ // When both numbers are negative we add the absolute values and then reverse the sign
59
+ ? ReverseSign<SumPositives<NumberAbsolute<A>, NumberAbsolute<B>>>
60
+ // When the signs are different we can subtract the absolute values, remove the sign
61
+ // and then reverse the sign if the larger absolute value is negative
62
+ : NumberAbsolute<Subtract<NumberAbsolute<A>, NumberAbsolute<B>>> extends infer Result extends number
63
+ ? TupleMax<[NumberAbsolute<A>, NumberAbsolute<B>]> extends infer Max_ extends number
64
+ ? Max_ extends A | B
65
+ // The larger absolute value is positive, so the result is positive
66
+ ? Result
67
+ // The larger absolute value is negative, so the result is negative
68
+ : ReverseSign<Result>
69
+ : never
70
+ : never;
71
+
72
+ /**
73
+ Adds two positive numbers.
74
+ */
75
+ type SumPositives<A extends number, B extends number> =
76
+ [...BuildTuple<A>, ...BuildTuple<B>]['length'] extends infer Result extends number
77
+ ? Result
70
78
  : never;
@@ -18,6 +18,7 @@ declare namespace TsConfigJson {
18
18
  | 'ES2022'
19
19
  | 'ESNext'
20
20
  | 'Node16'
21
+ | 'Node18'
21
22
  | 'NodeNext'
22
23
  | 'Preserve'
23
24
  | 'None'
@@ -32,6 +33,7 @@ declare namespace TsConfigJson {
32
33
  | 'es2022'
33
34
  | 'esnext'
34
35
  | 'node16'
36
+ | 'node18'
35
37
  | 'nodenext'
36
38
  | 'preserve'
37
39
  | 'none';
@@ -1109,6 +1111,20 @@ declare namespace TsConfigJson {
1109
1111
  Suppress deprecation warnings
1110
1112
  */
1111
1113
  ignoreDeprecations?: CompilerOptions.IgnoreDeprecations;
1114
+
1115
+ /**
1116
+ Do not allow runtime constructs that are not part of ECMAScript.
1117
+
1118
+ @default false
1119
+ */
1120
+ erasableSyntaxOnly?: boolean;
1121
+
1122
+ /**
1123
+ Enable lib replacement.
1124
+
1125
+ @default true
1126
+ */
1127
+ libReplacement?: boolean;
1112
1128
  };
1113
1129
 
1114
1130
  namespace WatchOptions {
@@ -1160,12 +1176,12 @@ declare namespace TsConfigJson {
1160
1176
  synchronousWatchDirectory?: boolean;
1161
1177
 
1162
1178
  /**
1163
- Specifies a list of directories to exclude from watch
1179
+ Specifies a list of directories to exclude from watch.
1164
1180
  */
1165
1181
  excludeDirectories?: string[];
1166
1182
 
1167
1183
  /**
1168
- Specifies a list of files to exclude from watch
1184
+ Specifies a list of files to exclude from watch.
1169
1185
  */
1170
1186
  excludeFiles?: string[];
1171
1187
  };
package/source/words.d.ts CHANGED
@@ -1,11 +1,46 @@
1
- import type {IsLowerCase, IsNumeric, IsUpperCase, WordSeparators} from './internal';
1
+ import type {
2
+ IsLowerCase,
3
+ IsNumeric,
4
+ IsUpperCase,
5
+ WordSeparators,
6
+ } from './internal';
2
7
 
3
8
  type SkipEmptyWord<Word extends string> = Word extends '' ? [] : [Word];
4
9
 
5
- type RemoveLastCharacter<Sentence extends string, Character extends string> = Sentence extends `${infer LeftSide}${Character}`
10
+ type RemoveLastCharacter<
11
+ Sentence extends string,
12
+ Character extends string,
13
+ > = Sentence extends `${infer LeftSide}${Character}`
6
14
  ? SkipEmptyWord<LeftSide>
7
15
  : never;
8
16
 
17
+ /**
18
+ Words options.
19
+
20
+ @see {@link Words}
21
+ */
22
+ export type WordsOptions = {
23
+ /**
24
+ Split on numeric sequence.
25
+
26
+ @default true
27
+
28
+ @example
29
+ ```
30
+ type Example1 = Words<'p2pNetwork', {splitOnNumbers: true}>;
31
+ //=> ["p", "2", "p", "Network"]
32
+
33
+ type Example2 = Words<'p2pNetwork', {splitOnNumbers: false}>;
34
+ //=> ["p2p", "Network"]
35
+ ```
36
+ */
37
+ splitOnNumbers?: boolean;
38
+ };
39
+
40
+ type DefaultOptions = {
41
+ splitOnNumbers: true;
42
+ };
43
+
9
44
  /**
10
45
  Split a string (almost) like Lodash's `_.words()` function.
11
46
 
@@ -31,37 +66,53 @@ type Words3 = Words<'--hello the_world'>;
31
66
 
32
67
  type Words4 = Words<'lifeIs42'>;
33
68
  //=> ['life', 'Is', '42']
69
+
70
+ type Words5 = Words<'p2pNetwork', {splitOnNumbers: false}>;
71
+ //=> ['p2p', 'Network']
34
72
  ```
35
73
 
36
74
  @category Change case
37
75
  @category Template literal
38
76
  */
39
- export type Words<
77
+ export type Words<Sentence extends string, Options extends WordsOptions = {}> = WordsImplementation<Sentence, {
78
+ splitOnNumbers: Options['splitOnNumbers'] extends boolean ? Options['splitOnNumbers'] : DefaultOptions['splitOnNumbers'];
79
+ }>;
80
+
81
+ type WordsImplementation<
40
82
  Sentence extends string,
83
+ Options extends Required<WordsOptions>,
41
84
  LastCharacter extends string = '',
42
85
  CurrentWord extends string = '',
43
86
  > = Sentence extends `${infer FirstCharacter}${infer RemainingCharacters}`
44
87
  ? FirstCharacter extends WordSeparators
45
88
  // Skip word separator
46
- ? [...SkipEmptyWord<CurrentWord>, ...Words<RemainingCharacters>]
89
+ ? [...SkipEmptyWord<CurrentWord>, ...WordsImplementation<RemainingCharacters, Options>]
47
90
  : LastCharacter extends ''
48
91
  // Fist char of word
49
- ? Words<RemainingCharacters, FirstCharacter, FirstCharacter>
50
- // Case change: non-numeric to numeric, push word
92
+ ? WordsImplementation<RemainingCharacters, Options, FirstCharacter, FirstCharacter>
93
+ // Case change: non-numeric to numeric
51
94
  : [false, true] extends [IsNumeric<LastCharacter>, IsNumeric<FirstCharacter>]
52
- ? [...SkipEmptyWord<CurrentWord>, ...Words<RemainingCharacters, FirstCharacter, FirstCharacter>]
53
- // Case change: numeric to non-numeric, push word
95
+ ? Options['splitOnNumbers'] extends true
96
+ // Split on number: push word
97
+ ? [...SkipEmptyWord<CurrentWord>, ...WordsImplementation<RemainingCharacters, Options, FirstCharacter, FirstCharacter>]
98
+ // No split on number: concat word
99
+ : WordsImplementation<RemainingCharacters, Options, FirstCharacter, `${CurrentWord}${FirstCharacter}`>
100
+ // Case change: numeric to non-numeric
54
101
  : [true, false] extends [IsNumeric<LastCharacter>, IsNumeric<FirstCharacter>]
55
- ? [...SkipEmptyWord<CurrentWord>, ...Words<RemainingCharacters, FirstCharacter, FirstCharacter>]
102
+ ? Options['splitOnNumbers'] extends true
103
+ // Split on number: push word
104
+ ? [...SkipEmptyWord<CurrentWord>, ...WordsImplementation<RemainingCharacters, Options, FirstCharacter, FirstCharacter>]
105
+ // No split on number: concat word
106
+ : WordsImplementation<RemainingCharacters, Options, FirstCharacter, `${CurrentWord}${FirstCharacter}`>
56
107
  // No case change: concat word
57
108
  : [true, true] extends [IsNumeric<LastCharacter>, IsNumeric<FirstCharacter>]
58
- ? Words<RemainingCharacters, FirstCharacter, `${CurrentWord}${FirstCharacter}`>
109
+ ? WordsImplementation<RemainingCharacters, Options, FirstCharacter, `${CurrentWord}${FirstCharacter}`>
59
110
  // Case change: lower to upper, push word
60
111
  : [true, true] extends [IsLowerCase<LastCharacter>, IsUpperCase<FirstCharacter>]
61
- ? [...SkipEmptyWord<CurrentWord>, ...Words<RemainingCharacters, FirstCharacter, FirstCharacter>]
112
+ ? [...SkipEmptyWord<CurrentWord>, ...WordsImplementation<RemainingCharacters, Options, FirstCharacter, FirstCharacter>]
62
113
  // Case change: upper to lower, brings back the last character, push word
63
114
  : [true, true] extends [IsUpperCase<LastCharacter>, IsLowerCase<FirstCharacter>]
64
- ? [...RemoveLastCharacter<CurrentWord, LastCharacter>, ...Words<RemainingCharacters, FirstCharacter, `${LastCharacter}${FirstCharacter}`>]
115
+ ? [...RemoveLastCharacter<CurrentWord, LastCharacter>, ...WordsImplementation<RemainingCharacters, Options, FirstCharacter, `${LastCharacter}${FirstCharacter}`>]
65
116
  // No case change: concat word
66
- : Words<RemainingCharacters, FirstCharacter, `${CurrentWord}${FirstCharacter}`>
117
+ : WordsImplementation<RemainingCharacters, Options, FirstCharacter, `${CurrentWord}${FirstCharacter}`>
67
118
  : [...SkipEmptyWord<CurrentWord>];