Skip to content

Commit 3e9c2b5

Browse files
committed
feat(arr,fn,null,num,obj,prom,str,url,err,http,json,pat,ts,value):
import utils from Graffle
1 parent 2d3f7ad commit 3e9c2b5

36 files changed

+2843
-1040
lines changed

src/domains/arr/arr.ts

Lines changed: 123 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,50 @@ export type Any = readonly any[]
1212

1313
export type Empty = readonly []
1414

15+
// Note: UnionToTuple is exported from Union namespace as Union.ToTuple
16+
17+
/**
18+
* Check if all booleans in a tuple are true.
19+
*
20+
* @example
21+
* ```ts
22+
* type T1 = All<[true, true, true]> // true
23+
* type T2 = All<[true, false, true]> // false
24+
* ```
25+
*/
26+
export type All<$Tuple extends [...boolean[]]> = $Tuple[number] extends true ? true : false
27+
28+
/**
29+
* Check if a tuple has multiple elements.
30+
*
31+
* @example
32+
* ```ts
33+
* type T1 = IsTupleMultiple<[1, 2]> // true
34+
* type T2 = IsTupleMultiple<[1]> // false
35+
* ```
36+
*/
37+
export type IsTupleMultiple<$T> = $T extends [unknown, unknown, ...unknown[]] ? true : false
38+
39+
/**
40+
* Push a value onto a tuple.
41+
*
42+
* @example
43+
* ```ts
44+
* type T = Push<[1, 2], 3> // [1, 2, 3]
45+
* ```
46+
*/
47+
export type Push<$T extends any[], $V> = [...$T, $V]
48+
49+
/**
50+
* Get the first non-unknown, non-never element from a tuple.
51+
*/
52+
export type FirstNonUnknownNever<$T extends any[]> = $T extends [infer __first__, ...infer __rest__]
53+
? unknown extends __first__ ? 0 extends 1 & __first__ ? FirstNonUnknownNever<__rest__> // is any
54+
: FirstNonUnknownNever<__rest__> // is unknown
55+
: __first__ extends never ? FirstNonUnknownNever<__rest__>
56+
: __first__
57+
: never
58+
1559
/**
1660
* Empty array constant.
1761
* @deprecated Use `Array.empty` from Effect instead
@@ -26,6 +70,22 @@ export type Empty = readonly []
2670
*/
2771
export const empty: Empty = []
2872

73+
/**
74+
* Empty array constant (frozen).
75+
* Useful as a default value or sentinel.
76+
*
77+
* @example
78+
* ```ts
79+
* const arr = items ?? Arr.emptyArray
80+
* ```
81+
*/
82+
export const emptyArray = Object.freeze([] as const)
83+
84+
/**
85+
* Type for the empty array constant.
86+
*/
87+
export type EmptyArray = typeof emptyArray
88+
2989
//
3090
//
3191
//
@@ -34,4 +94,67 @@ export const empty: Empty = []
3494
//
3595
//
3696

97+
/**
98+
* Assert that a value is an array.
99+
* Throws a TypeError if the value is not an array.
100+
*
101+
* @param value - The value to check
102+
* @throws {TypeError} If the value is not an array
103+
*
104+
* @example
105+
* ```ts
106+
* function process(value: unknown) {
107+
* Arr.assert(value)
108+
* // value is now typed as unknown[]
109+
* value.forEach(item => console.log(item))
110+
* }
111+
* ```
112+
*/
113+
export function assert(value: unknown): asserts value is unknown[] {
114+
if (!Array.isArray(value)) {
115+
throw new TypeError(`Expected array but got ${typeof value}`)
116+
}
117+
}
118+
119+
/**
120+
* Type-safe array includes check that narrows the type of the value.
121+
* Unlike the standard `includes`, this provides proper type narrowing.
122+
*
123+
* @param array - The array to search in
124+
* @param value - The unknown value to search for
125+
* @returns True if the value is in the array, with type narrowing
126+
* @example
127+
* ```ts
128+
* const fruits = ['apple', 'banana', 'orange'] as const
129+
* const value: unknown = 'apple'
130+
*
131+
* if (Arr.includes(fruits, value)) {
132+
* // value is now typed as 'apple' | 'banana' | 'orange'
133+
* }
134+
* ```
135+
*/
136+
export const includes = <$T>(array: $T[], value: unknown): value is $T => {
137+
return array.includes(value as any)
138+
}
139+
140+
// Note: partitionErrors is exported from ArrMut module (creates new arrays)
141+
142+
/**
143+
* Ensure a value is an array.
144+
* If the value is already an array, return it as-is.
145+
* Otherwise, wrap it in an array.
146+
*
147+
* @param value - The value to ensure as array
148+
* @returns An array containing the value(s)
149+
* @example
150+
* ```ts
151+
* Arr.ensure('hello') // ['hello']
152+
* Arr.ensure(['a', 'b']) // ['a', 'b']
153+
* Arr.ensure(42) // [42]
154+
* ```
155+
*/
156+
export const ensure = <$T>(value: $T | $T[]): $T[] => {
157+
return Array.isArray(value) ? value : [value]
158+
}
159+
37160
// TODO: Add immutable array operations that wrap ArrMut with copy semantics

src/domains/fn/base.ts

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,17 @@ export const is = Lang.typeGuard<AnyAny>(value => typeof value === Lang.TypeofTy
1616

1717
export type AnyAnyAsync = (...args: any[]) => Prom.AnyAny
1818

19+
/**
20+
* Extract the guarded type from a type guard function.
21+
*
22+
* @example
23+
* ```ts
24+
* const isString = (x: any): x is string => typeof x === 'string'
25+
* type T = GuardedType<typeof isString> // string
26+
* ```
27+
*/
28+
export type GuardedType<$T> = $T extends (x: any) => x is infer __u__ ? __u__ : never
29+
1930
/**
2031
* Modify function such that it only returns the given type.
2132
* Assumes that the given type is among the possible return types of the function.

src/domains/null/$$.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,3 @@
1+
export * from './null.js'
12
export { Eq } from './traits/eq.js'
23
export { Type } from './traits/type.js'

src/domains/null/null.ts

Lines changed: 83 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,83 @@
1+
/**
2+
* Makes a type nullable by adding `null` to it.
3+
*
4+
* @template $T - The type to make nullable
5+
*
6+
* @example
7+
* ```ts
8+
* type RequiredString = string
9+
* type NullableString = Maybe<RequiredString> // string | null
10+
*
11+
* // Useful for database-like values
12+
* function findUser(id: string): Maybe<User> {
13+
* // Returns User | null
14+
* return users.find(u => u.id === id) ?? null
15+
* }
16+
* ```
17+
*
18+
* @example
19+
* ```ts
20+
* // Using with object properties
21+
* interface DatabaseRecord {
22+
* deletedAt: Maybe<Date>
23+
* updatedBy: Maybe<string>
24+
* }
25+
*
26+
* const record: DatabaseRecord = {
27+
* deletedAt: null,
28+
* updatedBy: 'admin'
29+
* }
30+
* ```
31+
*/
32+
export type Maybe<$T> = $T | null
33+
34+
/**
35+
* Excludes `null` from a type.
36+
*
37+
* This utility type removes `null` from a union type, leaving all other types intact.
38+
* Useful for creating stricter type definitions or working with required values.
39+
*
40+
* @template T - The type to exclude `null` from
41+
*
42+
* @example
43+
* ```ts
44+
* type MaybeString = string | null
45+
* type DefinitelyString = Exclude<MaybeString> // string
46+
*
47+
* type Complex = string | number | null | undefined
48+
* type WithoutNull = Exclude<Complex> // string | number | undefined
49+
*
50+
* // Only null becomes never
51+
* type JustNull = Exclude<null> // never
52+
* ```
53+
*
54+
* @example
55+
* ```ts
56+
* // Using with function parameters
57+
* function requireValue<T>(value: T): Exclude<T> {
58+
* if (value === null) {
59+
* throw new Error('Value cannot be null')
60+
* }
61+
* return value as Exclude<T>
62+
* }
63+
*
64+
* const result = requireValue('hello' as string | null)
65+
* // result: string
66+
* ```
67+
*
68+
* @example
69+
* ```ts
70+
* // Using with object properties
71+
* interface User {
72+
* id: string
73+
* name: string | null
74+
* email: string | null | undefined
75+
* }
76+
*
77+
* type RequiredUser = {
78+
* [K in keyof User]: Exclude<User[K]>
79+
* }
80+
* // RequiredUser: { id: string; name: string; email: string | undefined }
81+
* ```
82+
*/
83+
export type Exclude<T> = T extends null ? never : T

src/domains/num/operations.ts

Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -162,3 +162,66 @@ export const modOn = <T extends number>(dividend: T) => <U extends NonZero>(divi
162162
export const modWith = <U extends NonZero>(divisor: U) => <T extends number>(dividend: T): Mod<T, U> => {
163163
return mod(dividend, divisor)
164164
}
165+
166+
/**
167+
* Number literal type.
168+
*/
169+
export type NumberLiteral = number
170+
171+
/**
172+
* Add one to a number literal type.
173+
*/
174+
export type PlusOne<$n extends NumberLiteral> = [
175+
1,
176+
2,
177+
3,
178+
4,
179+
5,
180+
6,
181+
7,
182+
8,
183+
9,
184+
10,
185+
11,
186+
12,
187+
13,
188+
14,
189+
15,
190+
16,
191+
17,
192+
18,
193+
19,
194+
20,
195+
21,
196+
][
197+
$n
198+
]
199+
200+
/**
201+
* Subtract one from a number literal type.
202+
*/
203+
export type MinusOne<$n extends NumberLiteral> = [
204+
-1,
205+
0,
206+
1,
207+
2,
208+
3,
209+
4,
210+
5,
211+
6,
212+
7,
213+
8,
214+
9,
215+
10,
216+
11,
217+
12,
218+
13,
219+
14,
220+
15,
221+
16,
222+
17,
223+
18,
224+
19,
225+
][
226+
$n
227+
]

src/domains/num/range.test.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -288,8 +288,8 @@ describe('interpolation', () => {
288288
return
289289
}
290290

291-
// Use relative tolerance for large numbers
292-
const tolerance = Math.max(1e-7, Math.abs(avgValue) * 1e-10)
291+
// Use relative tolerance for large numbers (1e-6 to account for floating point precision)
292+
const tolerance = Math.max(1e-6, Math.abs(avgValue) * 1e-10)
293293
expect(Math.abs(midValue - avgValue)).toBeLessThan(tolerance)
294294
},
295295
),

src/domains/obj/$$.ts

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,10 @@
1+
export * from './filter.js'
2+
export * from './get.js'
3+
export * from './merge.js'
14
export * from './obj.js'
5+
export * from './predicates.js'
26
export * from './property-signature.js'
37
export { Eq } from './traits/eq.js'
48
export { Type } from './traits/type.js'
5-
export * from './type-utils.js'
9+
export * from './type.js'
10+
export * from './update.js'

0 commit comments

Comments
 (0)