functype 0.9.3 → 0.9.4

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
- 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-Q45K2ZSV.mjs';import'../chunk-BQJB6CCW.mjs';import'../chunk-ECL55NTP.mjs';//# sourceMappingURL=index.mjs.map
2
2
  //# sourceMappingURL=index.mjs.map
@@ -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-Q45K2ZSV.mjs';import'../chunk-BQJB6CCW.mjs';import'../chunk-ECL55NTP.mjs';//# sourceMappingURL=index.mjs.map
2
2
  //# sourceMappingURL=index.mjs.map
package/dist/index.d.ts CHANGED
@@ -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
@@ -40,7 +43,7 @@ interface ValidatedBrand<K extends string, T> {
40
43
  * // value is Brand<"Email", string>
41
44
  * }
42
45
  */
43
- declare function ValidatedBrand<K extends string, T>(brand: K, validate: (value: T) => boolean): ValidatedBrand<K, T>;
46
+ declare function ValidatedBrand<K extends string, T>(brand: K, validate: (value: T) => boolean): ValidatedBrandCompanion<K, T>;
44
47
  /**
45
48
  * Positive number brand (> 0)
46
49
  * @example
@@ -48,17 +51,17 @@ declare function ValidatedBrand<K extends string, T>(brand: K, validate: (value:
48
51
  * const invalid = PositiveNumber.of(-5) // None
49
52
  * const checked = PositiveNumber.from(0) // Left("Invalid PositiveNumber: validation failed")
50
53
  */
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>>;
54
+ declare const PositiveNumber: ValidatedBrandCompanion<"PositiveNumber", number>;
55
+ declare const NonNegativeNumber: ValidatedBrandCompanion<"NonNegativeNumber", number>;
56
+ declare const IntegerNumber: ValidatedBrandCompanion<"IntegerNumber", number>;
57
+ declare const PositiveInteger: ValidatedBrandCompanion<"PositiveInteger", Brand<"PositiveNumber", number>>;
55
58
  /**
56
59
  * Non-empty string brand
57
60
  * @example
58
61
  * const name = NonEmptyString.of("John") // Some(Brand<"NonEmptyString", string>)
59
62
  * const empty = NonEmptyString.of("") // None
60
63
  */
61
- declare const NonEmptyString: ValidatedBrand<"NonEmptyString", string>;
64
+ declare const NonEmptyString: ValidatedBrandCompanion<"NonEmptyString", string>;
62
65
  /**
63
66
  * Email address brand with basic validation
64
67
  * @example
@@ -73,10 +76,10 @@ declare const NonEmptyString: ValidatedBrand<"NonEmptyString", string>;
73
76
  * .getOrElse("Invalid email address")
74
77
  * }
75
78
  */
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>;
79
+ declare const EmailAddress: ValidatedBrandCompanion<"EmailAddress", string>;
80
+ declare const UrlString: ValidatedBrandCompanion<"UrlString", string>;
81
+ declare const UUID: ValidatedBrandCompanion<"UUID", string>;
82
+ declare const ISO8601Date: ValidatedBrandCompanion<"ISO8601Date", string>;
80
83
  /**
81
84
  * Create a number brand with min/max bounds
82
85
  * @example
@@ -89,7 +92,7 @@ declare const ISO8601Date: ValidatedBrand<"ISO8601Date", string>;
89
92
  * const httpPort = Port.unsafeOf(80) // Brand<"Port", number>
90
93
  * // Port.unsafeOf(70000) // throws Error
91
94
  */
92
- declare function BoundedNumber(brand: string, min: number, max: number): ValidatedBrand<string, number>;
95
+ declare function BoundedNumber(brand: string, min: number, max: number): ValidatedBrandCompanion<string, number>;
93
96
  /**
94
97
  * Create a string brand with length constraints
95
98
  * @example
@@ -98,7 +101,7 @@ declare function BoundedNumber(brand: string, min: number, max: number): Validat
98
101
  * const tooShort = Username.of("jo") // None
99
102
  * const tooLong = Username.of("verylongusernamethatexceedslimit") // None
100
103
  */
101
- declare function BoundedString(brand: string, minLength: number, maxLength: number): ValidatedBrand<string, string>;
104
+ declare function BoundedString(brand: string, minLength: number, maxLength: number): ValidatedBrandCompanion<string, string>;
102
105
  /**
103
106
  * Create a string brand that matches a regex pattern
104
107
  * @example
@@ -112,7 +115,7 @@ declare function BoundedString(brand: string, minLength: number, maxLength: numb
112
115
  * .map(p => formatPhoneNumber(p))
113
116
  * .getOrElse("Invalid phone number")
114
117
  */
115
- declare function PatternString(brand: string, pattern: RegExp): ValidatedBrand<string, string>;
118
+ declare function PatternString(brand: string, pattern: RegExp): ValidatedBrandCompanion<string, string>;
116
119
 
117
120
  /**
118
121
  * Creates a function-object hybrid similar to Scala's companion objects.
@@ -1734,4 +1737,4 @@ declare const Stack: (<A extends Type>(values?: A[]) => Stack<A>) & {
1734
1737
  fromBinary: <A>(binary: string) => Stack<A>;
1735
1738
  };
1736
1739
 
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 };
1740
+ 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,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-Q45K2ZSV.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
2
2
  //# sourceMappingURL=index.mjs.map
@@ -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-Q45K2ZSV.mjs';import'../chunk-BQJB6CCW.mjs';import'../chunk-ECL55NTP.mjs';//# sourceMappingURL=index.mjs.map
2
2
  //# sourceMappingURL=index.mjs.map
@@ -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-Q45K2ZSV.mjs';import'../chunk-BQJB6CCW.mjs';import'../chunk-ECL55NTP.mjs';//# sourceMappingURL=index.mjs.map
2
2
  //# sourceMappingURL=index.mjs.map
@@ -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-Q45K2ZSV.mjs';import'../chunk-BQJB6CCW.mjs';import'../chunk-ECL55NTP.mjs';//# sourceMappingURL=index.mjs.map
2
2
  //# sourceMappingURL=index.mjs.map
@@ -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-Q45K2ZSV.mjs';import'../chunk-BQJB6CCW.mjs';import'../chunk-ECL55NTP.mjs';//# sourceMappingURL=index.mjs.map
2
2
  //# sourceMappingURL=index.mjs.map
@@ -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{_ as Try}from'../chunk-Q45K2ZSV.mjs';import'../chunk-BQJB6CCW.mjs';import'../chunk-ECL55NTP.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.4",
4
4
  "type": "module",
5
5
  "description": "A smallish functional library for TypeScript",
6
6
  "author": "jordan.burke@gmail.com",
@@ -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.