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.
- package/dist/{Either-i_F6B_IB.d.ts → Either-Dd58ar32.d.ts} +10 -0
- package/dist/branded/index.d.ts +1 -1
- package/dist/branded/index.mjs +1 -1
- package/dist/{chunk-4WBZOSQH.mjs → chunk-6KFGLFLL.mjs} +25 -25
- package/dist/chunk-6KFGLFLL.mjs.map +1 -0
- package/dist/{chunk-ECL55NTP.mjs → chunk-YBBRJTHY.mjs} +2 -2
- package/dist/chunk-YBBRJTHY.mjs.map +1 -0
- package/dist/either/index.d.ts +1 -1
- package/dist/either/index.mjs +1 -1
- package/dist/fpromise/index.d.ts +1 -1
- package/dist/fpromise/index.mjs +1 -1
- package/dist/index.d.ts +39 -22
- package/dist/index.mjs +1 -1
- package/dist/list/index.d.ts +1 -1
- package/dist/list/index.mjs +1 -1
- package/dist/map/index.d.ts +1 -1
- package/dist/map/index.mjs +1 -1
- package/dist/option/index.d.ts +1 -1
- package/dist/option/index.mjs +1 -1
- package/dist/set/index.d.ts +1 -1
- package/dist/set/index.mjs +1 -1
- package/dist/try/index.d.ts +1 -1
- package/dist/try/index.mjs +1 -1
- package/package.json +7 -7
- package/readme/BRAND_MIGRATION_GUIDE.md +230 -0
- package/dist/chunk-4WBZOSQH.mjs.map +0 -1
- package/dist/chunk-ECL55NTP.mjs.map +0 -1
|
@@ -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-
|
|
2
|
-
//# sourceMappingURL=chunk-
|
|
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"]}
|
package/dist/either/index.d.ts
CHANGED
|
@@ -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-
|
|
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';
|
package/dist/either/index.mjs
CHANGED
|
@@ -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-
|
|
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
|
package/dist/fpromise/index.d.ts
CHANGED
package/dist/fpromise/index.mjs
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
export{K as FPromise,J as FPromiseCompanion}from'../chunk-
|
|
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-
|
|
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-
|
|
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
|
|
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<
|
|
18
|
-
readonly from: (value: T) => Either<string,
|
|
19
|
-
readonly unsafeOf: (value: T) =>
|
|
20
|
-
readonly is: (value: unknown) => value is
|
|
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) =>
|
|
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):
|
|
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:
|
|
52
|
-
declare const NonNegativeNumber:
|
|
53
|
-
declare const IntegerNumber:
|
|
54
|
-
declare const PositiveInteger:
|
|
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:
|
|
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:
|
|
77
|
-
declare const UrlString:
|
|
78
|
-
declare const UUID:
|
|
79
|
-
declare const ISO8601Date:
|
|
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):
|
|
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):
|
|
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):
|
|
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
|
|
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
|
package/dist/list/index.d.ts
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
export { L as List } from '../Either-
|
|
1
|
+
export { L as List } from '../Either-Dd58ar32.js';
|
|
2
2
|
import '../Serializable-CK9upOU0.js';
|
package/dist/list/index.mjs
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
export{h as List}from'../chunk-
|
|
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
|
package/dist/map/index.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { T as Traversable, C as Collection, O as Option } from '../Either-
|
|
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
|
|
package/dist/map/index.mjs
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
export{da as Map}from'../chunk-
|
|
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
|
package/dist/option/index.d.ts
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
export { N as None, O as Option, m as OptionConstructor, S as Some } from '../Either-
|
|
1
|
+
export { N as None, O as Option, m as OptionConstructor, S as Some } from '../Either-Dd58ar32.js';
|
|
2
2
|
import '../Serializable-CK9upOU0.js';
|
package/dist/option/index.mjs
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
export{b as None,d as Option,c as OptionConstructor,a as Some}from'../chunk-
|
|
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
|
package/dist/set/index.d.ts
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
export { n as Set } from '../Either-
|
|
1
|
+
export { n as Set } from '../Either-Dd58ar32.js';
|
|
2
2
|
import '../Serializable-CK9upOU0.js';
|
package/dist/set/index.mjs
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
export{e as Set}from'../chunk-
|
|
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
|
package/dist/try/index.d.ts
CHANGED
package/dist/try/index.mjs
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
export{
|
|
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
|
+
"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.
|
|
19
|
-
"@typescript-eslint/eslint-plugin": "^8.
|
|
20
|
-
"@typescript-eslint/parser": "^8.
|
|
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-
|
|
28
|
-
"eslint-plugin-prettier": "^5.5.
|
|
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.
|
|
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.
|