functype 0.9.3 → 0.9.5

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.
@@ -1,2 +1,2 @@
1
- function e(n,r){return r}function t(n){return n}function d(n,r){return n!=null}function a(n){return r=>e(n,r)}var s=n=>r=>e(n,r),o=n=>r=>e(n,r),K=n=>r=>e(n,r);export{e as a,t as b,d as c,a as d,s as e,o as f,K as g};//# sourceMappingURL=chunk-ECL55NTP.mjs.map
2
- //# sourceMappingURL=chunk-ECL55NTP.mjs.map
1
+ function e(n,r){return r}function t(n){return n}function d(n,r){return n!=null}function a(n){return r=>e(n,r)}var s=n=>r=>e(n,r),o=n=>r=>e(n,r),K=n=>r=>e(n,r);export{e as a,t as b,d as c,a as d,s as e,o as f,K as g};//# sourceMappingURL=chunk-YBBRJTHY.mjs.map
2
+ //# sourceMappingURL=chunk-YBBRJTHY.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/branded/Brand.ts"],"names":["Brand","_brand","value","unbrand","branded","hasBrand","createBrander","brand","BrandedString","BrandedNumber","BrandedBoolean"],"mappings":"AAqBO,SAASA,CAAAA,CAA2BC,CAAAA,CAAWC,CAAAA,CAAuB,CAG3E,OAAOA,CACT,CAOO,SAASC,CAAAA,CAA6BC,CAAAA,CAAyB,CAEpE,OAAOA,CACT,CAaO,SAASC,CAAAA,CAA8BH,CAAAA,CAAgBD,CAAAA,CAAiC,CAG7F,OAAOC,CAAAA,EAAU,IACnB,CAOO,SAASI,CAAAA,CAAmCC,CAAAA,CAAU,CAC3D,OAAQL,CAAAA,EAA0BF,CAAAA,CAAMO,CAAAA,CAAOL,CAAK,CACtD,KAQaM,CAAAA,CACQD,CAAAA,EAClBL,CAAAA,EACCF,CAAAA,CAAMO,CAAAA,CAAOL,CAAK,EAETO,CAAAA,CACQF,CAAAA,EAClBL,CAAAA,EACCF,CAAAA,CAAMO,CAAAA,CAAOL,CAAK,CAAA,CAETQ,CAAAA,CACQH,CAAAA,EAClBL,CAAAA,EACCF,CAAAA,CAAMO,CAAAA,CAAOL,CAAK","file":"chunk-YBBRJTHY.mjs","sourcesContent":["// Phantom type brand - exists only at compile time\n// Must use type alias (not interface) because we need intersection with primitives\nexport type Brand<K extends string, T> = T & {\n readonly __brand: K\n}\n\n// Utility type to extract the underlying type from a branded type\nexport type Unbrand<T> = T extends Brand<string, infer U> ? U : T\n\n// Utility type to extract the brand from a branded type\nexport type ExtractBrand<T> = T extends Brand<infer K, unknown> ? K : never\n\n/**\n * Brand is a utility for creating nominal typing in TypeScript.\n * It creates phantom types that exist only at compile time.\n * At runtime, the branded value IS the primitive value.\n *\n * @param _brand\n * @param value - The value to brand\n * @returns The value with phantom type brand\n */\nexport function Brand<K extends string, T>(_brand: K, value: T): Brand<K, T> {\n // Just return the value with a type assertion\n // No runtime modification - the brand exists only in TypeScript\n return value as Brand<K, T>\n}\n\n/**\n * Helper to remove a brand from a value\n * @param branded - The branded value\n * @returns The original value without the brand\n */\nexport function unbrand<K extends string, T>(branded: Brand<K, T>): T {\n // Since branded values ARE their primitives, just return as-is\n return branded as unknown as T\n}\n\n/**\n * Type guard for checking if a value has a specific brand\n * @param value - The value to check\n * @param _brand - The brand to check for (unused at runtime)\n * @returns True if the value has the specified brand\n *\n * Note: Since brands are phantom types that exist only at compile time,\n * this function can only provide a runtime approximation. It always returns true\n * for non-null values, as we have no way to actually check the brand at runtime.\n * This function is primarily for API consistency and documentation purposes.\n */\nexport function hasBrand<K extends string, T>(value: unknown, _brand: K): value is Brand<K, T> {\n // In a phantom type system, we can't actually check the brand at runtime\n // We can only verify the value exists\n return value !== null && value !== undefined\n}\n\n/**\n * Create a branded type constructor for a specific brand\n * @param brand - The brand name\n * @returns A function that brands values with the specified brand\n */\nexport function createBrander<K extends string, T>(brand: K) {\n return (value: T): Brand<K, T> => Brand(brand, value)\n}\n\n// Common branded primitive types\nexport type BrandedString<K extends string> = Brand<K, string>\nexport type BrandedNumber<K extends string> = Brand<K, number>\nexport type BrandedBoolean<K extends string> = Brand<K, boolean>\n\n// Factory for common primitive branded types\nexport const BrandedString =\n <K extends string>(brand: K) =>\n (value: string): BrandedString<K> =>\n Brand(brand, value)\n\nexport const BrandedNumber =\n <K extends string>(brand: K) =>\n (value: number): BrandedNumber<K> =>\n Brand(brand, value)\n\nexport const BrandedBoolean =\n <K extends string>(brand: K) =>\n (value: boolean): BrandedBoolean<K> =>\n Brand(brand, value)\n"]}
@@ -1,2 +1,2 @@
1
- export { E as Either, c as Left, R as Right, b as TestEither, f as TypeCheckLeft, e as TypeCheckRight, d as isLeft, i as isRight, t as tryCatch, g as tryCatchAsync } from '../Either-i_F6B_IB.js';
1
+ export { E as Either, c as Left, R as Right, b as TestEither, f as TypeCheckLeft, e as TypeCheckRight, d as isLeft, i as isRight, t as tryCatch, g as tryCatchAsync } from '../Either-Dd58ar32.js';
2
2
  import '../Serializable-CK9upOU0.js';
@@ -1,2 +1,2 @@
1
- export{q as Either,j as Left,i as Right,o as TypeCheckLeft,n as TypeCheckRight,l as isLeft,k as isRight,m as tryCatch,p as tryCatchAsync}from'../chunk-4WBZOSQH.mjs';import'../chunk-BQJB6CCW.mjs';import'../chunk-ECL55NTP.mjs';//# sourceMappingURL=index.mjs.map
1
+ export{q as Either,j as Left,i as Right,o as TypeCheckLeft,n as TypeCheckRight,l as isLeft,k as isRight,m as tryCatch,p as tryCatchAsync}from'../chunk-6KFGLFLL.mjs';import'../chunk-BQJB6CCW.mjs';import'../chunk-YBBRJTHY.mjs';//# sourceMappingURL=index.mjs.map
2
2
  //# sourceMappingURL=index.mjs.map
@@ -1,4 +1,4 @@
1
- import { E as Either } from '../Either-i_F6B_IB.js';
1
+ import { E as Either } from '../Either-Dd58ar32.js';
2
2
  import { T as Type } from '../Serializable-CK9upOU0.js';
3
3
 
4
4
  /**
@@ -1,2 +1,2 @@
1
- export{K as FPromise,J as FPromiseCompanion}from'../chunk-4WBZOSQH.mjs';import'../chunk-BQJB6CCW.mjs';import'../chunk-ECL55NTP.mjs';//# sourceMappingURL=index.mjs.map
1
+ export{K as FPromise,J as FPromiseCompanion}from'../chunk-6KFGLFLL.mjs';import'../chunk-BQJB6CCW.mjs';import'../chunk-YBBRJTHY.mjs';//# sourceMappingURL=index.mjs.map
2
2
  //# sourceMappingURL=index.mjs.map
package/dist/index.d.ts CHANGED
@@ -1,7 +1,7 @@
1
1
  import { Brand } from './branded/index.js';
2
2
  export { BrandedBoolean, BrandedNumber, BrandedString, ExtractBrand, Unbrand, createBrander, hasBrand, unbrand } from './branded/index.js';
3
- import { O as Option, E as Either, L as List, F as FunctypeBase, a as Extractable, T as Traversable, M as Matchable } from './Either-i_F6B_IB.js';
4
- export { A as Applicative, q as AsyncMonad, C as Collection, o as CollectionOps, p as ContainerOps, r as Functor, j as Functype, k as FunctypeCollection, c as Left, l as MatchableUtils, s as Monad, N as None, m as OptionConstructor, R as Right, n as Set, S as Some, b as TestEither, f as TypeCheckLeft, e as TypeCheckRight, h as isExtractable, d as isLeft, i as isRight, t as tryCatch, g as tryCatchAsync } from './Either-i_F6B_IB.js';
3
+ import { O as Option, E as Either, L as List, F as FunctypeBase, a as Extractable, T as Traversable, M as Matchable } from './Either-Dd58ar32.js';
4
+ export { A as Applicative, q as AsyncMonad, C as Collection, o as CollectionOps, p as ContainerOps, r as Functor, j as Functype, k as FunctypeCollection, c as Left, l as MatchableUtils, s as Monad, N as None, m as OptionConstructor, R as Right, n as Set, S as Some, b as TestEither, f as TypeCheckLeft, e as TypeCheckRight, h as isExtractable, d as isLeft, i as isRight, t as tryCatch, g as tryCatchAsync } from './Either-Dd58ar32.js';
5
5
  import { T as Type, a as Typeable, F as Foldable, P as Pipe, S as Serializable } from './Serializable-CK9upOU0.js';
6
6
  export { E as ExtractTag, b as SerializationMethods, c as TypeableParams, i as isTypeable } from './Serializable-CK9upOU0.js';
7
7
  import { FPromise } from './fpromise/index.js';
@@ -11,16 +11,19 @@ export { TypeNames } from './try/index.js';
11
11
  export { Map, SafeTraversable } from './map/index.js';
12
12
  export { Tuple } from './tuple/index.js';
13
13
 
14
- interface ValidatedBrand<K extends string, T> {
14
+ interface ValidatedBrandCompanion<K extends string, T> {
15
15
  readonly brand: K;
16
16
  readonly validate: (value: T) => boolean;
17
- readonly of: (value: T) => Option<Brand<K, T>>;
18
- readonly from: (value: T) => Either<string, Brand<K, T>>;
19
- readonly unsafeOf: (value: T) => Brand<K, T>;
20
- readonly is: (value: unknown) => value is Brand<K, T>;
17
+ readonly of: (value: T) => Option<ValidatedBrand<K, T>>;
18
+ readonly from: (value: T) => Either<string, ValidatedBrand<K, T>>;
19
+ readonly unsafeOf: (value: T) => ValidatedBrand<K, T>;
20
+ readonly is: (value: unknown) => value is ValidatedBrand<K, T>;
21
21
  readonly unwrap: (branded: Brand<K, T>) => T;
22
- readonly refine: <K2 extends string>(brand: K2, validate: (value: Brand<K, T>) => boolean) => ValidatedBrand<K2, Brand<K, T>>;
22
+ readonly refine: <K2 extends string>(brand: K2, validate: (value: Brand<K, T>) => boolean) => ValidatedBrandCompanion<K2, Brand<K, T>>;
23
23
  }
24
+ type ValidatedBrand<K extends string, T> = Brand<K, T> & {
25
+ readonly __validated: true;
26
+ };
24
27
  /**
25
28
  * Create a validated brand with runtime validation
26
29
  * @example
@@ -39,8 +42,22 @@ interface ValidatedBrand<K extends string, T> {
39
42
  * if (Email.is(value)) {
40
43
  * // value is Brand<"Email", string>
41
44
  * }
45
+ *
46
+ * @example
47
+ * // Best Practice: Use same brand name for seamless conversion
48
+ * // ValidatedBrand extends Brand, so when using the same brand name,
49
+ * // no casting is needed for conversion
50
+ * const ValidatedUserId = ValidatedBrand("UserId", (s: string) => s.length > 0)
51
+ * type ValidatedUserId = ReturnType<typeof ValidatedUserId.of> extends Option<infer T> ? T : never
52
+ * type UserId = Brand<"UserId", string>
53
+ *
54
+ * const toSimpleUserId = (id: ValidatedUserId): UserId => id // No cast needed!
55
+ *
56
+ * // Avoid different brand names which require casting:
57
+ * // ❌ ValidatedBrand("ValidatedUserId", ...) + Brand<"UserId", string>
58
+ * // ✅ ValidatedBrand("UserId", ...) + Brand<"UserId", string>
42
59
  */
43
- declare function ValidatedBrand<K extends string, T>(brand: K, validate: (value: T) => boolean): ValidatedBrand<K, T>;
60
+ declare function ValidatedBrand<K extends string, T>(brand: K, validate: (value: T) => boolean): ValidatedBrandCompanion<K, T>;
44
61
  /**
45
62
  * Positive number brand (> 0)
46
63
  * @example
@@ -48,17 +65,17 @@ declare function ValidatedBrand<K extends string, T>(brand: K, validate: (value:
48
65
  * const invalid = PositiveNumber.of(-5) // None
49
66
  * const checked = PositiveNumber.from(0) // Left("Invalid PositiveNumber: validation failed")
50
67
  */
51
- declare const PositiveNumber: ValidatedBrand<"PositiveNumber", number>;
52
- declare const NonNegativeNumber: ValidatedBrand<"NonNegativeNumber", number>;
53
- declare const IntegerNumber: ValidatedBrand<"IntegerNumber", number>;
54
- declare const PositiveInteger: ValidatedBrand<"PositiveInteger", Brand<"PositiveNumber", number>>;
68
+ declare const PositiveNumber: ValidatedBrandCompanion<"PositiveNumber", number>;
69
+ declare const NonNegativeNumber: ValidatedBrandCompanion<"NonNegativeNumber", number>;
70
+ declare const IntegerNumber: ValidatedBrandCompanion<"IntegerNumber", number>;
71
+ declare const PositiveInteger: ValidatedBrandCompanion<"PositiveInteger", Brand<"PositiveNumber", number>>;
55
72
  /**
56
73
  * Non-empty string brand
57
74
  * @example
58
75
  * const name = NonEmptyString.of("John") // Some(Brand<"NonEmptyString", string>)
59
76
  * const empty = NonEmptyString.of("") // None
60
77
  */
61
- declare const NonEmptyString: ValidatedBrand<"NonEmptyString", string>;
78
+ declare const NonEmptyString: ValidatedBrandCompanion<"NonEmptyString", string>;
62
79
  /**
63
80
  * Email address brand with basic validation
64
81
  * @example
@@ -73,10 +90,10 @@ declare const NonEmptyString: ValidatedBrand<"NonEmptyString", string>;
73
90
  * .getOrElse("Invalid email address")
74
91
  * }
75
92
  */
76
- declare const EmailAddress: ValidatedBrand<"EmailAddress", string>;
77
- declare const UrlString: ValidatedBrand<"UrlString", string>;
78
- declare const UUID: ValidatedBrand<"UUID", string>;
79
- declare const ISO8601Date: ValidatedBrand<"ISO8601Date", string>;
93
+ declare const EmailAddress: ValidatedBrandCompanion<"EmailAddress", string>;
94
+ declare const UrlString: ValidatedBrandCompanion<"UrlString", string>;
95
+ declare const UUID: ValidatedBrandCompanion<"UUID", string>;
96
+ declare const ISO8601Date: ValidatedBrandCompanion<"ISO8601Date", string>;
80
97
  /**
81
98
  * Create a number brand with min/max bounds
82
99
  * @example
@@ -89,7 +106,7 @@ declare const ISO8601Date: ValidatedBrand<"ISO8601Date", string>;
89
106
  * const httpPort = Port.unsafeOf(80) // Brand<"Port", number>
90
107
  * // Port.unsafeOf(70000) // throws Error
91
108
  */
92
- declare function BoundedNumber(brand: string, min: number, max: number): ValidatedBrand<string, number>;
109
+ declare function BoundedNumber(brand: string, min: number, max: number): ValidatedBrandCompanion<string, number>;
93
110
  /**
94
111
  * Create a string brand with length constraints
95
112
  * @example
@@ -98,7 +115,7 @@ declare function BoundedNumber(brand: string, min: number, max: number): Validat
98
115
  * const tooShort = Username.of("jo") // None
99
116
  * const tooLong = Username.of("verylongusernamethatexceedslimit") // None
100
117
  */
101
- declare function BoundedString(brand: string, minLength: number, maxLength: number): ValidatedBrand<string, string>;
118
+ declare function BoundedString(brand: string, minLength: number, maxLength: number): ValidatedBrandCompanion<string, string>;
102
119
  /**
103
120
  * Create a string brand that matches a regex pattern
104
121
  * @example
@@ -112,7 +129,7 @@ declare function BoundedString(brand: string, minLength: number, maxLength: numb
112
129
  * .map(p => formatPhoneNumber(p))
113
130
  * .getOrElse("Invalid phone number")
114
131
  */
115
- declare function PatternString(brand: string, pattern: RegExp): ValidatedBrand<string, string>;
132
+ declare function PatternString(brand: string, pattern: RegExp): ValidatedBrandCompanion<string, string>;
116
133
 
117
134
  /**
118
135
  * Creates a function-object hybrid similar to Scala's companion objects.
@@ -1734,4 +1751,4 @@ declare const Stack: (<A extends Type>(values?: A[]) => Stack<A>) & {
1734
1751
  fromBinary: <A>(binary: string) => Stack<A>;
1735
1752
  };
1736
1753
 
1737
- export { type Async, Base, BoundedNumber, BoundedString, Brand, type CancellationToken, type CancellationTokenSource, Companion, Cond, ESMap, type ESMapType, Either, type EitherKind, EmailAddress, type ErrorChainElement, type ErrorCode, type ErrorFormatterOptions, type ErrorMessage, type ErrorStatus, type ErrorWithTaskInfo, Extractable, FPromise, type FieldValidation, Foldable, FoldableUtils, type FormValidation, FunctypeBase, HKT, ISO8601Date, Identity, IntegerNumber, type Kind, Lazy, LazyList, Lazy as LazyType, List, type ListKind, Match, Matchable, NAME, NonEmptyString, NonNegativeNumber, Option, type OptionKind, ParseError, PatternString, Pipe, PositiveInteger, PositiveNumber, Ref, Ref as RefType, Serializable, Stack, type Sync, type TaggedThrowable, Task, type TaskErrorInfo, TaskException, type TaskInfo, type TaskParams, TaskResult, Throwable, type ThrowableType, Traversable, Try, type TryKind, Type, Typeable, TypedError, type TypedErrorContext, UUID, type UniversalContainer, UrlString, ValidatedBrand, Validation, type ValidationRule, type Validator, Valuable, type ValuableParams, createCancellationTokenSource, createErrorSerializer, formatError, formatStackTrace, isTaggedThrowable, safeStringify };
1754
+ export { type Async, Base, BoundedNumber, BoundedString, Brand, type CancellationToken, type CancellationTokenSource, Companion, Cond, ESMap, type ESMapType, Either, type EitherKind, EmailAddress, type ErrorChainElement, type ErrorCode, type ErrorFormatterOptions, type ErrorMessage, type ErrorStatus, type ErrorWithTaskInfo, Extractable, FPromise, type FieldValidation, Foldable, FoldableUtils, type FormValidation, FunctypeBase, HKT, ISO8601Date, Identity, IntegerNumber, type Kind, Lazy, LazyList, Lazy as LazyType, List, type ListKind, Match, Matchable, NAME, NonEmptyString, NonNegativeNumber, Option, type OptionKind, ParseError, PatternString, Pipe, PositiveInteger, PositiveNumber, Ref, Ref as RefType, Serializable, Stack, type Sync, type TaggedThrowable, Task, type TaskErrorInfo, TaskException, type TaskInfo, type TaskParams, TaskResult, Throwable, type ThrowableType, Traversable, Try, type TryKind, Type, Typeable, TypedError, type TypedErrorContext, UUID, type UniversalContainer, UrlString, ValidatedBrand, type ValidatedBrandCompanion, Validation, type ValidationRule, type Validator, Valuable, type ValuableParams, createCancellationTokenSource, createErrorSerializer, formatError, formatStackTrace, isTaggedThrowable, safeStringify };
package/dist/index.mjs CHANGED
@@ -1,2 +1,2 @@
1
- export{G as Base,B as BoundedNumber,C as BoundedString,E as Cond,ca as ESMap,q as Either,x as EmailAddress,K as FPromise,J as FPromiseCompanion,Z as FoldableUtils,$ as HKT,A as ISO8601Date,aa as Identity,u as IntegerNumber,ba as Lazy,W as LazyList,j as Left,h as List,da as Map,F as Match,ea as MatchableUtils,H as NAME,w as NonEmptyString,t as NonNegativeNumber,b as None,d as Option,c as OptionConstructor,U as ParseError,D as PatternString,v as PositiveInteger,s as PositiveNumber,fa as Ref,i as Right,e as Set,a as Some,ha as Stack,P as Task,M as TaskException,N as TaskResult,I as Throwable,_ as Try,o as TypeCheckLeft,n as TypeCheckRight,f as Typeable,V as TypedError,z as UUID,y as UrlString,r as ValidatedBrand,X as Validation,ga as Valuable,O as createCancellationTokenSource,T as createErrorSerializer,S as formatError,R as formatStackTrace,Y as isExtractable,l as isLeft,k as isRight,L as isTaggedThrowable,g as isTypeable,Q as safeStringify,m as tryCatch,p as tryCatchAsync}from'./chunk-4WBZOSQH.mjs';export{a as Companion,b as Tuple}from'./chunk-BQJB6CCW.mjs';export{a as Brand,g as BrandedBoolean,f as BrandedNumber,e as BrandedString,d as createBrander,c as hasBrand,b as unbrand}from'./chunk-ECL55NTP.mjs';//# sourceMappingURL=index.mjs.map
1
+ export{G as Base,B as BoundedNumber,C as BoundedString,E as Cond,ca as ESMap,q as Either,x as EmailAddress,K as FPromise,J as FPromiseCompanion,Z as FoldableUtils,_ as HKT,A as ISO8601Date,$ as Identity,u as IntegerNumber,ba as Lazy,W as LazyList,j as Left,h as List,da as Map,F as Match,ea as MatchableUtils,H as NAME,w as NonEmptyString,t as NonNegativeNumber,b as None,d as Option,c as OptionConstructor,U as ParseError,D as PatternString,v as PositiveInteger,s as PositiveNumber,fa as Ref,i as Right,e as Set,a as Some,ga as Stack,P as Task,M as TaskException,N as TaskResult,I as Throwable,aa as Try,o as TypeCheckLeft,n as TypeCheckRight,f as Typeable,V as TypedError,z as UUID,y as UrlString,r as ValidatedBrand,X as Validation,ha as Valuable,O as createCancellationTokenSource,T as createErrorSerializer,S as formatError,R as formatStackTrace,Y as isExtractable,l as isLeft,k as isRight,L as isTaggedThrowable,g as isTypeable,Q as safeStringify,m as tryCatch,p as tryCatchAsync}from'./chunk-6KFGLFLL.mjs';export{a as Companion,b as Tuple}from'./chunk-BQJB6CCW.mjs';export{a as Brand,g as BrandedBoolean,f as BrandedNumber,e as BrandedString,d as createBrander,c as hasBrand,b as unbrand}from'./chunk-YBBRJTHY.mjs';//# sourceMappingURL=index.mjs.map
2
2
  //# sourceMappingURL=index.mjs.map
@@ -1,2 +1,2 @@
1
- export { L as List } from '../Either-i_F6B_IB.js';
1
+ export { L as List } from '../Either-Dd58ar32.js';
2
2
  import '../Serializable-CK9upOU0.js';
@@ -1,2 +1,2 @@
1
- export{h as List}from'../chunk-4WBZOSQH.mjs';import'../chunk-BQJB6CCW.mjs';import'../chunk-ECL55NTP.mjs';//# sourceMappingURL=index.mjs.map
1
+ export{h as List}from'../chunk-6KFGLFLL.mjs';import'../chunk-BQJB6CCW.mjs';import'../chunk-YBBRJTHY.mjs';//# sourceMappingURL=index.mjs.map
2
2
  //# sourceMappingURL=index.mjs.map
@@ -1,4 +1,4 @@
1
- import { T as Traversable, C as Collection, O as Option } from '../Either-i_F6B_IB.js';
1
+ import { T as Traversable, C as Collection, O as Option } from '../Either-Dd58ar32.js';
2
2
  import { a as Typeable, S as Serializable, P as Pipe, F as Foldable, T as Type } from '../Serializable-CK9upOU0.js';
3
3
  import { Tuple } from '../tuple/index.js';
4
4
 
@@ -1,2 +1,2 @@
1
- export{da as Map}from'../chunk-4WBZOSQH.mjs';import'../chunk-BQJB6CCW.mjs';import'../chunk-ECL55NTP.mjs';//# sourceMappingURL=index.mjs.map
1
+ export{da as Map}from'../chunk-6KFGLFLL.mjs';import'../chunk-BQJB6CCW.mjs';import'../chunk-YBBRJTHY.mjs';//# sourceMappingURL=index.mjs.map
2
2
  //# sourceMappingURL=index.mjs.map
@@ -1,2 +1,2 @@
1
- export { N as None, O as Option, m as OptionConstructor, S as Some } from '../Either-i_F6B_IB.js';
1
+ export { N as None, O as Option, m as OptionConstructor, S as Some } from '../Either-Dd58ar32.js';
2
2
  import '../Serializable-CK9upOU0.js';
@@ -1,2 +1,2 @@
1
- export{b as None,d as Option,c as OptionConstructor,a as Some}from'../chunk-4WBZOSQH.mjs';import'../chunk-BQJB6CCW.mjs';import'../chunk-ECL55NTP.mjs';//# sourceMappingURL=index.mjs.map
1
+ export{b as None,d as Option,c as OptionConstructor,a as Some}from'../chunk-6KFGLFLL.mjs';import'../chunk-BQJB6CCW.mjs';import'../chunk-YBBRJTHY.mjs';//# sourceMappingURL=index.mjs.map
2
2
  //# sourceMappingURL=index.mjs.map
@@ -1,2 +1,2 @@
1
- export { n as Set } from '../Either-i_F6B_IB.js';
1
+ export { n as Set } from '../Either-Dd58ar32.js';
2
2
  import '../Serializable-CK9upOU0.js';
@@ -1,2 +1,2 @@
1
- export{e as Set}from'../chunk-4WBZOSQH.mjs';import'../chunk-BQJB6CCW.mjs';import'../chunk-ECL55NTP.mjs';//# sourceMappingURL=index.mjs.map
1
+ export{e as Set}from'../chunk-6KFGLFLL.mjs';import'../chunk-BQJB6CCW.mjs';import'../chunk-YBBRJTHY.mjs';//# sourceMappingURL=index.mjs.map
2
2
  //# sourceMappingURL=index.mjs.map
@@ -1,4 +1,4 @@
1
- import { F as FunctypeBase, a as Extractable, E as Either } from '../Either-i_F6B_IB.js';
1
+ import { F as FunctypeBase, a as Extractable, E as Either } from '../Either-Dd58ar32.js';
2
2
  import { P as Pipe, T as Type } from '../Serializable-CK9upOU0.js';
3
3
 
4
4
  /**
@@ -1,2 +1,2 @@
1
- export{_ as Try}from'../chunk-4WBZOSQH.mjs';import'../chunk-BQJB6CCW.mjs';import'../chunk-ECL55NTP.mjs';//# sourceMappingURL=index.mjs.map
1
+ export{aa as Try}from'../chunk-6KFGLFLL.mjs';import'../chunk-BQJB6CCW.mjs';import'../chunk-YBBRJTHY.mjs';//# sourceMappingURL=index.mjs.map
2
2
  //# sourceMappingURL=index.mjs.map
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "functype",
3
- "version": "0.9.3",
3
+ "version": "0.9.5",
4
4
  "type": "module",
5
5
  "description": "A smallish functional library for TypeScript",
6
6
  "author": "jordan.burke@gmail.com",
@@ -15,17 +15,17 @@
15
15
  "@eslint/compat": "^1.3.1",
16
16
  "@eslint/eslintrc": "^3.3.1",
17
17
  "@eslint/js": "^9.32.0",
18
- "@types/node": "^22.16.5",
19
- "@typescript-eslint/eslint-plugin": "^8.38.0",
20
- "@typescript-eslint/parser": "^8.38.0",
18
+ "@types/node": "^22.17.0",
19
+ "@typescript-eslint/eslint-plugin": "^8.39.0",
20
+ "@typescript-eslint/parser": "^8.39.0",
21
21
  "@vitest/coverage-v8": "^3.2.4",
22
22
  "@vitest/ui": "^3.2.4",
23
23
  "cross-env": "^10.0.0",
24
24
  "eslint": "^9.32.0",
25
25
  "eslint-config-prettier": "^10.1.8",
26
26
  "eslint-plugin-functional": "^9.0.2",
27
- "eslint-plugin-import": "^2.32.0",
28
- "eslint-plugin-prettier": "^5.5.3",
27
+ "eslint-plugin-functype": "1.1.0",
28
+ "eslint-plugin-prettier": "^5.5.4",
29
29
  "eslint-plugin-simple-import-sort": "^12.1.1",
30
30
  "fast-check": "^4.2.0",
31
31
  "globals": "^16.3.0",
@@ -33,7 +33,7 @@
33
33
  "rimraf": "^6.0.1",
34
34
  "ts-node": "^10.9.2",
35
35
  "tsup": "^8.5.0",
36
- "typedoc": "^0.28.8",
36
+ "typedoc": "^0.28.9",
37
37
  "typescript": "5.8.3",
38
38
  "vitest": "^3.2.4"
39
39
  },
@@ -0,0 +1,230 @@
1
+ # Brand Type Migration Guide
2
+
3
+ This guide helps you migrate from the old object-based Brand implementation to the new phantom type implementation.
4
+
5
+ ## What Changed?
6
+
7
+ ### Old Implementation (Object Wrappers)
8
+
9
+ - Branded values were **objects** with methods
10
+ - Required `.unbrand()` or `.unwrap()` to access the primitive value
11
+ - `typeof brandedString === "object"`
12
+ - Object overhead and indexed properties
13
+
14
+ ### New Implementation (Phantom Types)
15
+
16
+ - Branded values **ARE** the primitive values
17
+ - No unwrapping needed - use directly
18
+ - `typeof brandedString === "string"`
19
+ - Zero runtime overhead
20
+
21
+ ## Migration Steps
22
+
23
+ ### 1. Remove `.unbrand()` and `.unwrap()` calls
24
+
25
+ **Before:**
26
+
27
+ ```typescript
28
+ const userId = Brand("UserId", "user-123")
29
+ const id = userId.unbrand() // Had to unwrap
30
+ console.log(`User: ${userId.unbrand()}`)
31
+ ```
32
+
33
+ **After:**
34
+
35
+ ```typescript
36
+ const userId = Brand("UserId", "user-123")
37
+ const id = userId // It IS the string!
38
+ console.log(`User: ${userId}`)
39
+ ```
40
+
41
+ ### 2. Remove `.toString()` for custom formatting
42
+
43
+ **Before:**
44
+
45
+ ```typescript
46
+ const userId = Brand("UserId", "user-123")
47
+ console.log(userId.toString()) // "UserId(user-123)"
48
+ ```
49
+
50
+ **After:**
51
+
52
+ ```typescript
53
+ const userId = Brand("UserId", "user-123")
54
+ console.log(userId.toString()) // "user-123" - standard string method
55
+ console.log(userId) // "user-123"
56
+ ```
57
+
58
+ ### 3. Update ValidatedBrand usage
59
+
60
+ **Before:**
61
+
62
+ ```typescript
63
+ const email = EmailAddress.of("user@example.com")
64
+ if (!email.isEmpty) {
65
+ const branded = email.get()
66
+ sendEmail(branded.unbrand()) // Had to unwrap
67
+ }
68
+ ```
69
+
70
+ **After:**
71
+
72
+ ```typescript
73
+ const email = EmailAddress.of("user@example.com")
74
+ if (!email.isEmpty) {
75
+ const branded = email.get()
76
+ sendEmail(branded) // It IS a string!
77
+ }
78
+ ```
79
+
80
+ ### 4. Update refine validators
81
+
82
+ **Before:**
83
+
84
+ ```typescript
85
+ const SmallPositive = PositiveNumber.refine("SmallPositive", (n) => {
86
+ return n.unbrand() < 100 // Had to unwrap
87
+ })
88
+ ```
89
+
90
+ **After:**
91
+
92
+ ```typescript
93
+ const SmallPositive = PositiveNumber.refine("SmallPositive", (n) => {
94
+ return n < 100 // n IS a number!
95
+ })
96
+ ```
97
+
98
+ ### 5. Remove type assertions for external APIs
99
+
100
+ **Before:**
101
+
102
+ ```typescript
103
+ const config = {
104
+ userId: userId.unbrand(),
105
+ port: port.unbrand(),
106
+ }
107
+ await api.request(config)
108
+ ```
109
+
110
+ **After:**
111
+
112
+ ```typescript
113
+ const config = {
114
+ userId, // Already a string!
115
+ port, // Already a number!
116
+ }
117
+ await api.request(config)
118
+ ```
119
+
120
+ ## Common Patterns
121
+
122
+ ### Working with Option/Either
123
+
124
+ When extracting branded values from Option or Either, you may need to adjust default values:
125
+
126
+ **Before:**
127
+
128
+ ```typescript
129
+ const email = EmailAddress.of(input)
130
+ const value = email.map((e) => e.unbrand()).getOrElse("")
131
+ ```
132
+
133
+ **After (Option 1 - Keep branded type):**
134
+
135
+ ```typescript
136
+ const email = EmailAddress.of(input)
137
+ const value = email.getOrElse("" as Brand<"EmailAddress", string>)
138
+ ```
139
+
140
+ **After (Option 2 - Use fold for clean extraction):**
141
+
142
+ ```typescript
143
+ const email = EmailAddress.of(input)
144
+ const value = email.fold(
145
+ () => "", // Default value
146
+ (e) => e, // e IS already a string!
147
+ )
148
+ ```
149
+
150
+ ### Type-safe functions
151
+
152
+ Function signatures don't change, but implementation is cleaner:
153
+
154
+ **Before:**
155
+
156
+ ```typescript
157
+ function processUser(userId: Brand<"UserId", string>) {
158
+ const id = userId.unbrand()
159
+ return db.query(`SELECT * FROM users WHERE id = '${id}'`)
160
+ }
161
+ ```
162
+
163
+ **After:**
164
+
165
+ ```typescript
166
+ function processUser(userId: Brand<"UserId", string>) {
167
+ return db.query(`SELECT * FROM users WHERE id = '${userId}'`)
168
+ }
169
+ ```
170
+
171
+ ## Benefits After Migration
172
+
173
+ 1. **Better Performance**: No object allocation overhead
174
+ 2. **Cleaner Code**: No more `.unbrand()` calls everywhere
175
+ 3. **Natural JavaScript**: String interpolation, JSON serialization work directly
176
+ 4. **Smaller Bundles**: Less code to ship
177
+ 5. **Better Debugging**: Values show as primitives in debugger
178
+
179
+ ## Compatibility
180
+
181
+ ### If you need the old behavior
182
+
183
+ The `unbrand` utility function still exists for compatibility:
184
+
185
+ ```typescript
186
+ import { unbrand } from "@/branded"
187
+
188
+ const userId = Brand("UserId", "user-123")
189
+ const plain = unbrand(userId) // Works but unnecessary
190
+ ```
191
+
192
+ ValidatedBrand also provides an `unwrap` method:
193
+
194
+ ```typescript
195
+ const email = EmailAddress.unsafeOf("user@example.com")
196
+ const plain = EmailAddress.unwrap(email) // Works but unnecessary
197
+ ```
198
+
199
+ ## Quick Reference
200
+
201
+ | Old Code | New Code |
202
+ | ------------------------------------ | --------------------------- |
203
+ | `value.unbrand()` | `value` |
204
+ | `value.unwrap()` | `value` |
205
+ | `value.toString()` | `value` or `String(value)` |
206
+ | `typeof value === "object"` | `typeof value === "string"` |
207
+ | `${value.unbrand()}` | `${value}` |
208
+ | `JSON.stringify({id: id.unbrand()})` | `JSON.stringify({id})` |
209
+
210
+ ## Testing
211
+
212
+ Update your tests to expect primitives:
213
+
214
+ **Before:**
215
+
216
+ ```typescript
217
+ expect(typeof userId).toBe("object")
218
+ expect(userId.unbrand()).toBe("user-123")
219
+ ```
220
+
221
+ **After:**
222
+
223
+ ```typescript
224
+ expect(typeof userId).toBe("string")
225
+ expect(userId).toBe("user-123")
226
+ ```
227
+
228
+ ## Summary
229
+
230
+ The migration is mostly about **removing code**. Branded values now work exactly like their underlying primitive types, making the code cleaner and more performant. The type safety remains unchanged - you still get full compile-time protection against mixing different branded types.