ts-data-forge 1.0.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 (143) hide show
  1. package/LICENSE +201 -0
  2. package/README.md +534 -0
  3. package/package.json +101 -0
  4. package/src/array/array-utils-creation.test.mts +443 -0
  5. package/src/array/array-utils-modification.test.mts +197 -0
  6. package/src/array/array-utils-overload-type-error.test.mts +149 -0
  7. package/src/array/array-utils-reducing-value.test.mts +425 -0
  8. package/src/array/array-utils-search.test.mts +169 -0
  9. package/src/array/array-utils-set-op.test.mts +335 -0
  10. package/src/array/array-utils-slice-clamped.test.mts +113 -0
  11. package/src/array/array-utils-slicing.test.mts +316 -0
  12. package/src/array/array-utils-transformation.test.mts +790 -0
  13. package/src/array/array-utils-validation.test.mts +492 -0
  14. package/src/array/array-utils.mts +4000 -0
  15. package/src/array/array.test.mts +146 -0
  16. package/src/array/index.mts +2 -0
  17. package/src/array/tuple-utils.mts +519 -0
  18. package/src/array/tuple-utils.test.mts +518 -0
  19. package/src/collections/imap-mapped.mts +801 -0
  20. package/src/collections/imap-mapped.test.mts +860 -0
  21. package/src/collections/imap.mts +651 -0
  22. package/src/collections/imap.test.mts +932 -0
  23. package/src/collections/index.mts +6 -0
  24. package/src/collections/iset-mapped.mts +889 -0
  25. package/src/collections/iset-mapped.test.mts +1187 -0
  26. package/src/collections/iset.mts +682 -0
  27. package/src/collections/iset.test.mts +1084 -0
  28. package/src/collections/queue.mts +390 -0
  29. package/src/collections/queue.test.mts +282 -0
  30. package/src/collections/stack.mts +423 -0
  31. package/src/collections/stack.test.mts +225 -0
  32. package/src/expect-type.mts +206 -0
  33. package/src/functional/index.mts +4 -0
  34. package/src/functional/match.mts +300 -0
  35. package/src/functional/match.test.mts +177 -0
  36. package/src/functional/optional.mts +733 -0
  37. package/src/functional/optional.test.mts +619 -0
  38. package/src/functional/pipe.mts +212 -0
  39. package/src/functional/pipe.test.mts +85 -0
  40. package/src/functional/result.mts +1134 -0
  41. package/src/functional/result.test.mts +777 -0
  42. package/src/globals.d.mts +38 -0
  43. package/src/guard/has-key.mts +119 -0
  44. package/src/guard/has-key.test.mts +219 -0
  45. package/src/guard/index.mts +7 -0
  46. package/src/guard/is-non-empty-string.mts +108 -0
  47. package/src/guard/is-non-empty-string.test.mts +91 -0
  48. package/src/guard/is-non-null-object.mts +106 -0
  49. package/src/guard/is-non-null-object.test.mts +90 -0
  50. package/src/guard/is-primitive.mts +165 -0
  51. package/src/guard/is-primitive.test.mts +102 -0
  52. package/src/guard/is-record.mts +153 -0
  53. package/src/guard/is-record.test.mts +112 -0
  54. package/src/guard/is-type.mts +450 -0
  55. package/src/guard/is-type.test.mts +496 -0
  56. package/src/guard/key-is-in.mts +163 -0
  57. package/src/guard/key-is-in.test.mts +19 -0
  58. package/src/index.mts +10 -0
  59. package/src/iterator/index.mts +1 -0
  60. package/src/iterator/range.mts +120 -0
  61. package/src/iterator/range.test.mts +33 -0
  62. package/src/json/index.mts +1 -0
  63. package/src/json/json.mts +711 -0
  64. package/src/json/json.test.mts +628 -0
  65. package/src/number/branded-types/finite-number.mts +354 -0
  66. package/src/number/branded-types/finite-number.test.mts +135 -0
  67. package/src/number/branded-types/index.mts +26 -0
  68. package/src/number/branded-types/int.mts +278 -0
  69. package/src/number/branded-types/int.test.mts +140 -0
  70. package/src/number/branded-types/int16.mts +192 -0
  71. package/src/number/branded-types/int16.test.mts +170 -0
  72. package/src/number/branded-types/int32.mts +193 -0
  73. package/src/number/branded-types/int32.test.mts +170 -0
  74. package/src/number/branded-types/non-negative-finite-number.mts +223 -0
  75. package/src/number/branded-types/non-negative-finite-number.test.mts +188 -0
  76. package/src/number/branded-types/non-negative-int16.mts +187 -0
  77. package/src/number/branded-types/non-negative-int16.test.mts +201 -0
  78. package/src/number/branded-types/non-negative-int32.mts +187 -0
  79. package/src/number/branded-types/non-negative-int32.test.mts +204 -0
  80. package/src/number/branded-types/non-zero-finite-number.mts +229 -0
  81. package/src/number/branded-types/non-zero-finite-number.test.mts +198 -0
  82. package/src/number/branded-types/non-zero-int.mts +167 -0
  83. package/src/number/branded-types/non-zero-int.test.mts +177 -0
  84. package/src/number/branded-types/non-zero-int16.mts +196 -0
  85. package/src/number/branded-types/non-zero-int16.test.mts +195 -0
  86. package/src/number/branded-types/non-zero-int32.mts +196 -0
  87. package/src/number/branded-types/non-zero-int32.test.mts +197 -0
  88. package/src/number/branded-types/non-zero-safe-int.mts +196 -0
  89. package/src/number/branded-types/non-zero-safe-int.test.mts +232 -0
  90. package/src/number/branded-types/non-zero-uint16.mts +189 -0
  91. package/src/number/branded-types/non-zero-uint16.test.mts +199 -0
  92. package/src/number/branded-types/non-zero-uint32.mts +189 -0
  93. package/src/number/branded-types/non-zero-uint32.test.mts +199 -0
  94. package/src/number/branded-types/positive-finite-number.mts +241 -0
  95. package/src/number/branded-types/positive-finite-number.test.mts +204 -0
  96. package/src/number/branded-types/positive-int.mts +304 -0
  97. package/src/number/branded-types/positive-int.test.mts +176 -0
  98. package/src/number/branded-types/positive-int16.mts +188 -0
  99. package/src/number/branded-types/positive-int16.test.mts +197 -0
  100. package/src/number/branded-types/positive-int32.mts +188 -0
  101. package/src/number/branded-types/positive-int32.test.mts +197 -0
  102. package/src/number/branded-types/positive-safe-int.mts +187 -0
  103. package/src/number/branded-types/positive-safe-int.test.mts +210 -0
  104. package/src/number/branded-types/positive-uint16.mts +188 -0
  105. package/src/number/branded-types/positive-uint16.test.mts +203 -0
  106. package/src/number/branded-types/positive-uint32.mts +188 -0
  107. package/src/number/branded-types/positive-uint32.test.mts +203 -0
  108. package/src/number/branded-types/safe-int.mts +291 -0
  109. package/src/number/branded-types/safe-int.test.mts +170 -0
  110. package/src/number/branded-types/safe-uint.mts +187 -0
  111. package/src/number/branded-types/safe-uint.test.mts +176 -0
  112. package/src/number/branded-types/uint.mts +179 -0
  113. package/src/number/branded-types/uint.test.mts +158 -0
  114. package/src/number/branded-types/uint16.mts +186 -0
  115. package/src/number/branded-types/uint16.test.mts +170 -0
  116. package/src/number/branded-types/uint32.mts +218 -0
  117. package/src/number/branded-types/uint32.test.mts +170 -0
  118. package/src/number/enum/index.mts +2 -0
  119. package/src/number/enum/int8.mts +344 -0
  120. package/src/number/enum/int8.test.mts +180 -0
  121. package/src/number/enum/uint8.mts +293 -0
  122. package/src/number/enum/uint8.test.mts +164 -0
  123. package/src/number/index.mts +4 -0
  124. package/src/number/num.mts +604 -0
  125. package/src/number/num.test.mts +242 -0
  126. package/src/number/refined-number-utils.mts +566 -0
  127. package/src/object/index.mts +1 -0
  128. package/src/object/object.mts +447 -0
  129. package/src/object/object.test.mts +124 -0
  130. package/src/others/cast-mutable.mts +113 -0
  131. package/src/others/cast-readonly.mts +192 -0
  132. package/src/others/cast-readonly.test.mts +89 -0
  133. package/src/others/if-then.mts +98 -0
  134. package/src/others/if-then.test.mts +75 -0
  135. package/src/others/index.mts +7 -0
  136. package/src/others/map-nullable.mts +172 -0
  137. package/src/others/map-nullable.test.mts +297 -0
  138. package/src/others/memoize-function.mts +196 -0
  139. package/src/others/memoize-function.test.mts +168 -0
  140. package/src/others/tuple.mts +160 -0
  141. package/src/others/tuple.test.mts +11 -0
  142. package/src/others/unknown-to-string.mts +215 -0
  143. package/src/others/unknown-to-string.test.mts +114 -0
@@ -0,0 +1,170 @@
1
+ import { expectType } from '../../expect-type.mjs';
2
+ import { asNonZeroUint32 } from './non-zero-uint32.mjs';
3
+ import { asUint32, isUint32, Uint32 } from './uint32.mjs';
4
+
5
+ describe('Uint32', () => {
6
+ describe('asUint32', () => {
7
+ test('accepts valid uint32 values', () => {
8
+ expect(() => asUint32(0)).not.toThrow();
9
+ expect(() => asUint32(1)).not.toThrow();
10
+ expect(() => asUint32(4294967295)).not.toThrow(); // 2^32 - 1
11
+ expect(() => asUint32(2147483648)).not.toThrow(); // 2^31
12
+ });
13
+
14
+ test('rejects values outside uint32 range', () => {
15
+ expect(() => asUint32(4294967296)).toThrow(TypeError); // 2^32
16
+ expect(() => asUint32(10000000000)).toThrow(TypeError);
17
+ });
18
+
19
+ test('rejects negative integers', () => {
20
+ expect(() => asUint32(-1)).toThrow(TypeError);
21
+ expect(() => asUint32(-42)).toThrow(TypeError);
22
+ });
23
+
24
+ test('rejects non-integers', () => {
25
+ expect(() => asUint32(Number.NaN)).toThrow(TypeError);
26
+ expect(() => asUint32(Number.POSITIVE_INFINITY)).toThrow(TypeError);
27
+ expect(() => asUint32(Number.NEGATIVE_INFINITY)).toThrow(TypeError);
28
+ expect(() => asUint32(1.2)).toThrow(TypeError);
29
+ expect(() => asUint32(-3.4)).toThrow(TypeError);
30
+ });
31
+
32
+ test('returns the same value for valid inputs', () => {
33
+ expect(asUint32(5)).toBe(5);
34
+ expect(asUint32(0)).toBe(0);
35
+ expect(asUint32(4294967295)).toBe(4294967295);
36
+ });
37
+
38
+ test.each([
39
+ { name: 'Number.NaN', value: Number.NaN },
40
+ { name: 'Number.POSITIVE_INFINITY', value: Number.POSITIVE_INFINITY },
41
+ { name: 'Number.NEGATIVE_INFINITY', value: Number.NEGATIVE_INFINITY },
42
+ { name: '1.2', value: 1.2 },
43
+ { name: '-3.4', value: -3.4 },
44
+ { name: '-1', value: -1 },
45
+ ] as const)(`asUint32($name) should throw a TypeError`, ({ value }) => {
46
+ expect(() => asUint32(value)).toThrow(
47
+ new TypeError(
48
+ `Expected a non-negative integer less than 2^32, got: ${value}`,
49
+ ),
50
+ );
51
+ });
52
+ });
53
+
54
+ describe('isUint32', () => {
55
+ test('correctly identifies uint32 values', () => {
56
+ expect(isUint32(0)).toBe(true);
57
+ expect(isUint32(1)).toBe(true);
58
+ expect(isUint32(4294967295)).toBe(true);
59
+ expect(isUint32(2147483648)).toBe(true);
60
+ });
61
+
62
+ test('correctly identifies values outside uint32 range', () => {
63
+ expect(isUint32(4294967296)).toBe(false);
64
+ expect(isUint32(10000000000)).toBe(false);
65
+ });
66
+
67
+ test('correctly identifies negative integers', () => {
68
+ expect(isUint32(-1)).toBe(false);
69
+ expect(isUint32(-42)).toBe(false);
70
+ });
71
+
72
+ test('correctly identifies non-integers', () => {
73
+ expect(isUint32(Number.NaN)).toBe(false);
74
+ expect(isUint32(Number.POSITIVE_INFINITY)).toBe(false);
75
+ expect(isUint32(Number.NEGATIVE_INFINITY)).toBe(false);
76
+ expect(isUint32(1.2)).toBe(false);
77
+ expect(isUint32(-3.4)).toBe(false);
78
+ });
79
+ });
80
+
81
+ describe('Uint32.is', () => {
82
+ test('same as isUint32 function', () => {
83
+ expect(Uint32.is(5)).toBe(isUint32(5));
84
+ expect(Uint32.is(4294967296)).toBe(isUint32(4294967296));
85
+ expect(Uint32.is(-1)).toBe(isUint32(-1));
86
+ });
87
+ });
88
+
89
+ describe('constants', () => {
90
+ test('MIN_VALUE and MAX_VALUE', () => {
91
+ expect(Uint32.MIN_VALUE).toBe(0);
92
+ expect(Uint32.MAX_VALUE).toBe(4294967295);
93
+ });
94
+ });
95
+
96
+ describe('mathematical operations', () => {
97
+ const a = asUint32(1000000);
98
+ const b = asUint32(500000);
99
+ const c = asUint32(0);
100
+
101
+ test('min and max', () => {
102
+ expect(Uint32.min(a, b)).toBe(500000);
103
+ expect(Uint32.max(a, b)).toBe(1000000);
104
+ expect(Uint32.min(a, c)).toBe(0);
105
+ expect(Uint32.max(a, c)).toBe(1000000);
106
+ });
107
+
108
+ test('add (with clamping to uint32 range)', () => {
109
+ const result = Uint32.add(asUint32(4294967000), asUint32(1000));
110
+ expect(result).toBe(4294967295); // clamped to max
111
+ expect(Uint32.add(a, b)).toBe(1500000);
112
+ });
113
+
114
+ test('sub (never goes below 0)', () => {
115
+ expect(Uint32.sub(a, b)).toBe(500000);
116
+ expect(Uint32.sub(b, a)).toBe(0); // clamped to 0
117
+ expect(Uint32.sub(c, a)).toBe(0); // clamped to 0
118
+ });
119
+
120
+ test('mul (with clamping to uint32 range)', () => {
121
+ const result = Uint32.mul(asUint32(100000), asUint32(100000));
122
+ expect(result).toBe(4294967295); // clamped to max
123
+ expect(Uint32.mul(asUint32(1000), asUint32(5))).toBe(5000);
124
+ });
125
+
126
+ test('div (floor division, never goes below 0)', () => {
127
+ expect(Uint32.div(a, asNonZeroUint32(500000))).toBe(2);
128
+ expect(Uint32.div(asUint32(7), asNonZeroUint32(3))).toBe(2);
129
+ expect(Uint32.div(asUint32(500000), asNonZeroUint32(1000000))).toBe(0); // floor(500000/1000000) = 0
130
+ });
131
+
132
+ test('pow (with clamping to uint32 range)', () => {
133
+ const result = Uint32.pow(asUint32(10000), asUint32(3));
134
+ expect(result).toBe(4294967295); // clamped to max
135
+ expect(Uint32.pow(asUint32(2), asUint32(3))).toBe(8);
136
+ });
137
+ });
138
+
139
+ describe('random', () => {
140
+ test('generates uint32 values within specified range', () => {
141
+ const min = 0;
142
+ const max = 20;
143
+
144
+ for (let i = 0; i < 10; i++) {
145
+ const result = Uint32.random(min, max);
146
+ expect(result).toBeGreaterThanOrEqual(min);
147
+ expect(result).toBeLessThanOrEqual(max);
148
+ expect(Uint32.is(result)).toBe(true);
149
+ expect(Number.isInteger(result)).toBe(true);
150
+ expect(result).toBeGreaterThanOrEqual(0);
151
+ }
152
+ });
153
+
154
+ test('generates values within Uint32 range', () => {
155
+ for (let i = 0; i < 10; i++) {
156
+ const result = Uint32.random(0, 30);
157
+ expect(result).toBeGreaterThanOrEqual(0);
158
+ expect(result).toBeLessThanOrEqual(4294967295);
159
+ }
160
+ });
161
+ });
162
+
163
+ describe('type assertions', () => {
164
+ test('type relationships', () => {
165
+ expectType<Uint32, number>('<=');
166
+
167
+ expectTypeOf(asUint32(1000000)).toExtend<Uint32>();
168
+ });
169
+ });
170
+ });
@@ -0,0 +1,2 @@
1
+ export * from './int8.mjs';
2
+ export * from './uint8.mjs';
@@ -0,0 +1,344 @@
1
+ import { expectType } from '../../expect-type.mjs';
2
+ import { TsVerifiedInternals } from '../refined-number-utils.mjs';
3
+
4
+ const typeNameInMessage = 'an integer in [-128, 127]';
5
+
6
+ const {
7
+ MIN_VALUE,
8
+ MAX_VALUE,
9
+ random: randomImpl,
10
+ is: isImpl,
11
+ castType: castTypeImpl,
12
+ clamp: clampImpl,
13
+ } = TsVerifiedInternals.RefinedNumberUtils.operatorsForInteger<
14
+ Int16,
15
+ -128,
16
+ 127
17
+ >({
18
+ integerOrSafeInteger: 'SafeInteger',
19
+ MIN_VALUE: -128,
20
+ MAX_VALUE: 127,
21
+ typeNameInMessage,
22
+ } as const);
23
+
24
+ /**
25
+ * Checks if a number is a valid Int8 (integer in [-128, 127]).
26
+ * @param x - The number to check
27
+ * @returns True if x is a valid Int8
28
+ */
29
+ const is = (x: number): x is Int8 => isImpl(x);
30
+
31
+ /**
32
+ * Converts a number to Int8, throwing an error if invalid.
33
+ * @param x - The number to convert
34
+ * @returns The number as Int8
35
+ * @throws TypeError if x is not a valid Int8
36
+ */
37
+ const castType = (x: number): Int8 =>
38
+ // eslint-disable-next-line @typescript-eslint/no-unsafe-type-assertion
39
+ castTypeImpl(x) as Int8;
40
+
41
+ /**
42
+ * Clamps a number to the Int8 range [-128, 127].
43
+ * @param a - The number to clamp
44
+ * @returns The clamped value as Int8
45
+ */
46
+ const clamp = (a: number): Int8 => castType(clampImpl(a));
47
+
48
+ /**
49
+ * Returns the absolute value of an Int8.
50
+ * @param x - The Int8 value
51
+ * @returns The absolute value
52
+ */
53
+ const abs = <N extends Int8>(x: N): AbsoluteValue<N> =>
54
+ // eslint-disable-next-line @typescript-eslint/no-unsafe-type-assertion
55
+ Math.abs(x) as unknown as AbsoluteValue<N>;
56
+
57
+ /**
58
+ * Returns the minimum of the given Int8 values.
59
+ * @param values - The Int8 values to compare
60
+ * @returns The minimum value
61
+ */
62
+ const min_ = (...values: readonly Int8[]): Int8 =>
63
+ castType(Math.min(...values));
64
+
65
+ /**
66
+ * Returns the maximum of the given Int8 values.
67
+ * @param values - The Int8 values to compare
68
+ * @returns The maximum value
69
+ */
70
+ const max_ = (...values: readonly Int8[]): Int8 =>
71
+ castType(Math.max(...values));
72
+
73
+ /**
74
+ * Raises x to the power of y, clamped to Int8 range.
75
+ * @param x - The base
76
+ * @param y - The exponent
77
+ * @returns x^y clamped to [-128, 127]
78
+ */
79
+ const pow = (x: Int8, y: Int8): Int8 => clamp(x ** y);
80
+
81
+ /**
82
+ * Adds two Int8 values, clamped to Int8 range.
83
+ * @param x - First operand
84
+ * @param y - Second operand
85
+ * @returns x + y clamped to [-128, 127]
86
+ */
87
+ const add = (x: Int8, y: Int8): Int8 => clamp(x + y);
88
+
89
+ /**
90
+ * Subtracts two Int8 values, clamped to Int8 range.
91
+ * @param x - First operand
92
+ * @param y - Second operand
93
+ * @returns x - y clamped to [-128, 127]
94
+ */
95
+ const sub = (x: Int8, y: Int8): Int8 => clamp(x - y);
96
+
97
+ /**
98
+ * Multiplies two Int8 values, clamped to Int8 range.
99
+ * @param x - First operand
100
+ * @param y - Second operand
101
+ * @returns x * y clamped to [-128, 127]
102
+ */
103
+ const mul = (x: Int8, y: Int8): Int8 => clamp(x * y);
104
+
105
+ /**
106
+ * Divides two Int8 values, clamped to Int8 range.
107
+ * @param x - The dividend
108
+ * @param y - The divisor (cannot be 0)
109
+ * @returns ⌊x / y⌋ clamped to [-128, 127]
110
+ */
111
+ const div = (x: Int8, y: Exclude<Int8, 0>): Int8 => clamp(Math.floor(x / y));
112
+
113
+ /**
114
+ * Generates a random Int8 value within the specified range.
115
+ * @param min - The minimum value (inclusive)
116
+ * @param max - The maximum value (inclusive)
117
+ * @returns A random Int8 between min and max
118
+ */
119
+ const random = (min: Int8, max: Int8): Int8 =>
120
+ castType(randomImpl(castTypeImpl(min), castTypeImpl(max)));
121
+
122
+ /**
123
+ * Type guard that checks if a value is an 8-bit signed integer.
124
+ *
125
+ * An Int8 is a signed integer in the range [-128, 127], representing
126
+ * values that fit in exactly 8 bits of memory.
127
+ *
128
+ * @param value - The value to check
129
+ * @returns `true` if the value is an Int8, `false` otherwise
130
+ *
131
+ * @example
132
+ * ```typescript
133
+ * isInt8(100); // true
134
+ * isInt8(-50); // true
135
+ * isInt8(127); // true (max value)
136
+ * isInt8(-128); // true (min value)
137
+ * isInt8(128); // false (exceeds max)
138
+ * isInt8(-129); // false (below min)
139
+ * isInt8(5.5); // false (not integer)
140
+ * ```
141
+ */
142
+ export const isInt8 = is;
143
+
144
+ /**
145
+ * Casts a number to an Int8 branded type.
146
+ *
147
+ * This function validates that the input is within the Int8 range [-128, 127]
148
+ * and is an integer, then returns it with the Int8 brand.
149
+ *
150
+ * @param value - The value to cast
151
+ * @returns The value as an Int8 branded type
152
+ * @throws {TypeError} If the value is not a valid 8-bit signed integer
153
+ *
154
+ * @example
155
+ * ```typescript
156
+ * const byte = asInt8(100); // Int8
157
+ * const max = asInt8(127); // Int8 (maximum value)
158
+ * const min = asInt8(-128); // Int8 (minimum value)
159
+ * const zero = asInt8(0); // Int8
160
+ *
161
+ * // These throw TypeError:
162
+ * // asInt8(128); // Exceeds maximum (127)
163
+ * // asInt8(-129); // Below minimum (-128)
164
+ * // asInt8(1.5); // Not an integer
165
+ * // asInt8(NaN); // Not a number
166
+ * ```
167
+ */
168
+ export const asInt8 = castType;
169
+
170
+ /**
171
+ * Namespace providing type-safe operations for Int8 (8-bit signed integer) branded types.
172
+ *
173
+ * Int8 represents signed integers in the range [-128, 127], equivalent to a signed
174
+ * byte in many programming languages. All operations automatically clamp results
175
+ * to stay within this range, preventing overflow/underflow issues.
176
+ *
177
+ * This type is useful for:
178
+ * - Binary data processing (signed bytes)
179
+ * - Small integer values with known bounds
180
+ * - Embedded systems programming
181
+ * - Memory-efficient integer storage
182
+ * - Image processing (signed pixel offsets)
183
+ *
184
+ * @example
185
+ * ```typescript
186
+ * // Basic usage
187
+ * const a = asInt8(100);
188
+ * const b = asInt8(50);
189
+ *
190
+ * // Arithmetic with automatic clamping
191
+ * const sum = Int8.add(a, b); // Int8 (127) - clamped to maximum
192
+ * const diff = Int8.sub(a, b); // Int8 (50)
193
+ * const product = Int8.mul(a, b); // Int8 (127) - clamped due to overflow
194
+ * const quotient = Int8.div(a, b); // Int8 (2)
195
+ *
196
+ * // Boundary handling
197
+ * const overflow = Int8.add(asInt8(127), asInt8(10)); // Int8 (127) - clamped
198
+ * const underflow = Int8.sub(asInt8(-128), asInt8(10)); // Int8 (-128) - clamped
199
+ *
200
+ * // Utility operations
201
+ * const clamped = Int8.clamp(200); // Int8 (127)
202
+ * const absolute = Int8.abs(asInt8(-100)); // Int8 (100)
203
+ * const minimum = Int8.min(a, b); // Int8 (50)
204
+ * const maximum = Int8.max(a, b); // Int8 (100)
205
+ *
206
+ * // Random generation
207
+ * const die = Int8.random(asInt8(1), asInt8(6)); // Random 1-6
208
+ * const offset = Int8.random(asInt8(-10), asInt8(10)); // Random ±10
209
+ * ```
210
+ */
211
+ export const Int8 = {
212
+ /**
213
+ * Type guard that checks if a value is an 8-bit signed integer.
214
+ *
215
+ * @param value - The value to check
216
+ * @returns `true` if the value is in range [-128, 127] and is an integer
217
+ *
218
+ * @see {@link isInt8} for usage examples
219
+ */
220
+ is,
221
+
222
+ /**
223
+ * The minimum value for an 8-bit signed integer.
224
+ * @readonly
225
+ */
226
+ MIN_VALUE,
227
+
228
+ /**
229
+ * The maximum value for an 8-bit signed integer.
230
+ * @readonly
231
+ */
232
+ MAX_VALUE,
233
+
234
+ /**
235
+ * Returns the minimum value from a list of Int8 values.
236
+ *
237
+ * @param values - The Int8 values to compare (at least one required)
238
+ * @returns The smallest value as an Int8
239
+ *
240
+ * @example
241
+ * ```typescript
242
+ * Int8.min(asInt8(50), asInt8(-30), asInt8(100)); // Int8 (-30)
243
+ * ```
244
+ */
245
+ min: min_,
246
+
247
+ /**
248
+ * Returns the maximum value from a list of Int8 values.
249
+ *
250
+ * @param values - The Int8 values to compare (at least one required)
251
+ * @returns The largest value as an Int8
252
+ *
253
+ * @example
254
+ * ```typescript
255
+ * Int8.max(asInt8(50), asInt8(-30), asInt8(100)); // Int8 (100)
256
+ * ```
257
+ */
258
+ max: max_,
259
+
260
+ /**
261
+ * Clamps a number to the Int8 range.
262
+ * @param value The number to clamp.
263
+ * @returns The value clamped to [-128, 127] as an Int8.
264
+ */
265
+ clamp,
266
+
267
+ /**
268
+ * Returns the absolute value of an Int8.
269
+ * @param value The Int8 value.
270
+ * @returns The absolute value as an Int8, clamped to valid range.
271
+ */
272
+ abs,
273
+
274
+ /**
275
+ * Generates a random Int8 value within the specified range (inclusive).
276
+ *
277
+ * Both bounds are inclusive. If min > max, they are automatically swapped.
278
+ *
279
+ * @param min - The minimum value (inclusive)
280
+ * @param max - The maximum value (inclusive)
281
+ * @returns A random Int8 in the range [min, max]
282
+ *
283
+ * @example
284
+ * ```typescript
285
+ * // Random signed byte
286
+ * const randomByte = Int8.random(Int8.MIN_VALUE, Int8.MAX_VALUE);
287
+ *
288
+ * // Random small range
289
+ * const dice = Int8.random(asInt8(1), asInt8(6)); // 1-6
290
+ *
291
+ * // Random offset
292
+ * const offset = Int8.random(asInt8(-10), asInt8(10)); // -10 to 10
293
+ * ```
294
+ */
295
+ random,
296
+
297
+ /**
298
+ * Raises an Int8 to the power of another Int8.
299
+ * @param a The base Int8.
300
+ * @param b The exponent Int8.
301
+ * @returns `a ** b` clamped to [-128, 127] as an Int8.
302
+ */
303
+ pow,
304
+
305
+ /**
306
+ * Adds two Int8 values.
307
+ * @param a The first Int8.
308
+ * @param b The second Int8.
309
+ * @returns `a + b` clamped to [-128, 127] as an Int8.
310
+ */
311
+ add,
312
+
313
+ /**
314
+ * Subtracts one Int8 from another.
315
+ * @param a The minuend Int8.
316
+ * @param b The subtrahend Int8.
317
+ * @returns `a - b` clamped to [-128, 127] as an Int8.
318
+ */
319
+ sub,
320
+
321
+ /**
322
+ * Multiplies two Int8 values.
323
+ * @param a The first Int8.
324
+ * @param b The second Int8.
325
+ * @returns `a * b` clamped to [-128, 127] as an Int8.
326
+ */
327
+ mul,
328
+
329
+ /**
330
+ * Divides one Int8 by another using floor division.
331
+ * @param a The dividend Int8.
332
+ * @param b The divisor Int8 (cannot be 0).
333
+ * @returns `⌊a / b⌋` clamped to [-128, 127] as an Int8.
334
+ */
335
+ div,
336
+ } as const;
337
+
338
+ expectType<
339
+ keyof typeof Int8,
340
+ keyof TsVerifiedInternals.RefinedNumberUtils.NumberClass<
341
+ Int16,
342
+ 'int' | 'range'
343
+ >
344
+ >('=');
@@ -0,0 +1,180 @@
1
+ import { expectType } from '../../expect-type.mjs';
2
+ import { Int8, asInt8, isInt8 } from './int8.mjs';
3
+
4
+ describe('Int8', () => {
5
+ describe('isInt8', () => {
6
+ test.each([
7
+ { value: -128, expected: true },
8
+ { value: 0, expected: true },
9
+ { value: 127, expected: true },
10
+ { value: -129, expected: false },
11
+ { value: 128, expected: false },
12
+ { value: 1.5, expected: false },
13
+ { value: Number.NaN, expected: false },
14
+ { value: Number.POSITIVE_INFINITY, expected: false },
15
+ { value: Number.NEGATIVE_INFINITY, expected: false },
16
+ ])('isInt8($value) should return $expected', ({ value, expected }) => {
17
+ expect(isInt8(value)).toBe(expected);
18
+ });
19
+ });
20
+
21
+ describe('asInt8', () => {
22
+ test.each([
23
+ { value: -128, expected: -128 },
24
+ { value: 0, expected: 0 },
25
+ { value: 127, expected: 127 },
26
+ ])('asInt8($value) should return $expected', ({ value, expected }) => {
27
+ expect(asInt8(value)).toBe(expected);
28
+ });
29
+
30
+ test.each([
31
+ { value: -129, name: '-129' },
32
+ { value: 128, name: '128' },
33
+ { value: 1.5, name: '1.5' },
34
+ { value: Number.NaN, name: 'NaN' },
35
+ { value: Number.POSITIVE_INFINITY, name: 'Infinity' },
36
+ { value: Number.NEGATIVE_INFINITY, name: '-Infinity' },
37
+ ])('asInt8($name) should throw TypeError', ({ value }) => {
38
+ expect(() => asInt8(value)).toThrow(TypeError);
39
+ });
40
+ });
41
+
42
+ describe('Int8.is', () => {
43
+ test('should be the same as isInt8', () => {
44
+ expect(Int8.is).toBe(isInt8);
45
+ });
46
+ });
47
+
48
+ describe('Int8.MIN_VALUE', () => {
49
+ test('should be -128', () => {
50
+ expect(Int8.MIN_VALUE).toBe(-128);
51
+ expectType<typeof Int8.MIN_VALUE, -128>('=');
52
+ });
53
+ });
54
+
55
+ describe('Int8.MAX_VALUE', () => {
56
+ test('should be 127', () => {
57
+ expect(Int8.MAX_VALUE).toBe(127);
58
+ expectType<typeof Int8.MAX_VALUE, 127>('=');
59
+ });
60
+ });
61
+
62
+ describe('Int8.clamp', () => {
63
+ test.each([
64
+ { value: -200, expected: -128 },
65
+ { value: -128, expected: -128 },
66
+ { value: 0, expected: 0 },
67
+ { value: 127, expected: 127 },
68
+ { value: 200, expected: 127 },
69
+ { value: 1.5, expected: 2 },
70
+ ])('Int8.clamp($value) should return $expected', ({ value, expected }) => {
71
+ expect(Int8.clamp(value)).toBe(expected);
72
+ });
73
+ });
74
+
75
+ describe('Int8.min', () => {
76
+ test('should return minimum value', () => {
77
+ expect(Int8.min(-100, -50, 10)).toBe(-100);
78
+ expect(Int8.min(1, 2, 3)).toBe(1);
79
+ });
80
+ });
81
+
82
+ describe('Int8.max', () => {
83
+ test('should return maximum value', () => {
84
+ expect(Int8.max(-100, -50, 10)).toBe(10);
85
+ expect(Int8.max(1, 2, 3)).toBe(3);
86
+ });
87
+ });
88
+
89
+ describe('Int8.abs', () => {
90
+ test.each([
91
+ { value: -100, expected: 100 },
92
+ { value: 0, expected: 0 },
93
+ { value: 50, expected: 50 },
94
+ { value: -128, expected: 128 },
95
+ ] as const)(
96
+ 'Int8.abs($value) should return $expected',
97
+ ({ value, expected }) => {
98
+ const result = Int8.abs(value);
99
+ expect(result).toBe(expected);
100
+ expectType<typeof result, typeof expected>('=');
101
+ },
102
+ );
103
+ });
104
+
105
+ describe('Int8.add', () => {
106
+ test.each([
107
+ { x: 10, y: 20, expected: 30 },
108
+ { x: 100, y: 50, expected: 127 }, // clamped to max
109
+ { x: -100, y: -50, expected: -128 }, // clamped to min
110
+ ] as const)(
111
+ 'Int8.add($x, $y) should return $expected',
112
+ ({ x, y, expected }) => {
113
+ expect(Int8.add(x, y)).toBe(expected);
114
+ },
115
+ );
116
+ });
117
+
118
+ describe('Int8.sub', () => {
119
+ test.each([
120
+ { x: 20, y: 10, expected: 10 },
121
+ { x: -100, y: 50, expected: -128 }, // clamped to min
122
+ { x: 100, y: -50, expected: 127 }, // clamped to max
123
+ ] as const)(
124
+ 'Int8.sub($x, $y) should return $expected',
125
+ ({ x, y, expected }) => {
126
+ expect(Int8.sub(x, y)).toBe(expected);
127
+ },
128
+ );
129
+ });
130
+
131
+ describe('Int8.mul', () => {
132
+ test.each([
133
+ { x: 5, y: 10, expected: 50 },
134
+ { x: 20, y: 10, expected: 127 }, // clamped to max
135
+ { x: -20, y: 10, expected: -128 }, // clamped to min
136
+ ] as const)(
137
+ 'Int8.mul($x, $y) should return $expected',
138
+ ({ x, y, expected }) => {
139
+ expect(Int8.mul(x, y)).toBe(expected);
140
+ },
141
+ );
142
+ });
143
+
144
+ describe('Int8.div', () => {
145
+ test.each([
146
+ { x: 100, y: 2, expected: 50 },
147
+ { x: 100, y: 3, expected: 33 }, // floor division
148
+ { x: -100, y: 3, expected: -34 },
149
+ ] as const)(
150
+ 'Int8.div($x, $y) should return $expected',
151
+ ({ x, y, expected }) => {
152
+ expect(Int8.div(x, y)).toBe(expected);
153
+ },
154
+ );
155
+ });
156
+
157
+ describe('Int8.pow', () => {
158
+ test.each([
159
+ { x: 2, y: 3, expected: 8 },
160
+ { x: 10, y: 3, expected: 127 }, // clamped to max
161
+ { x: -10, y: 3, expected: -128 }, // clamped to min
162
+ ] as const)(
163
+ 'Int8.pow($x, $y) should return $expected',
164
+ ({ x, y, expected }) => {
165
+ expect(Int8.pow(x, y)).toBe(expected);
166
+ },
167
+ );
168
+ });
169
+
170
+ describe('Int8.random', () => {
171
+ test('should generate value within range', () => {
172
+ const min = -10;
173
+ const max = 10;
174
+ const result = Int8.random(min, max);
175
+ expect(result).toBeGreaterThanOrEqual(min);
176
+ expect(result).toBeLessThanOrEqual(max);
177
+ expect(Number.isInteger(result)).toBe(true);
178
+ });
179
+ });
180
+ });