To me the real benefit of branded types is for branded primitives. That helps you prevent mixing up things that are represented by the same primitive type, like say relative and absolute paths, or different types of IDs. You really don't need the symbol - you can use an obscure name for the branding field. I think it helps the type self-document in errors and hover-overs if you use a descriptive name. I use branding…
do you need readonly with never?
TypeScript: Branded Types
111–120 of 209 posts
Re: TypeScript: Branded Types
#112Earlier quoted context omitted.
That's a weird objection, because Typescript classes are literally Javascript classes[1]. [1]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Refe...
There was plenty of outrage when classes were added to JS. “JS uses prototypal inheritance, not OOP!”
Re: TypeScript: Branded Types
#113Anytime I’ve come across the need to do this, I’ve found a class is a better and less complicated solution. I really like the pattern of value objects from Domain Driven Design. Create a class that stores the value, for example email address. In the class constructor, take a string and validate it. Then anywhere that you need a valid email address, have it accept an instance of the Email class. As far as I understand…
> Anytime I’ve come across the need to do this, I’ve found a class is a better and less complicated solution ... As far as I understand classes are the only real way to get nominal typing in TypeScript. How are classes going to help? As far as I understand TS is structural period. Ex this is valid (!): class Email { constructor(public email: String) {} } let x: Email = new Email('foo@foo.com') let y = { email: '73'}…
For example, these two classes are distinct to TypeScript:
class Name {
constructor(private value: string) {}
getValue() {
return this.value;
}
}
class Email {
constructor(private value: string) {}
getValue() {
return this.value;
}
}
const email: Email = new Name(“Tim”); // Error: (paraphrasing) conflicting private declaration of “value”.Re: TypeScript: Branded Types
#114Anytime I’ve come across the need to do this, I’ve found a class is a better and less complicated solution. I really like the pattern of value objects from Domain Driven Design. Create a class that stores the value, for example email address. In the class constructor, take a string and validate it. Then anywhere that you need a valid email address, have it accept an instance of the Email class. As far as I understand…
Yeah, classes are generally better than branded types for objects (unless you're just building discriminated unions). What's particularly better these days is that you get nominal typing and brand checks with standard private fields: class Foo { #brand; static isFoo(o): o is Foo { return #brand in o; } }
Re: TypeScript: Branded Types
#115Earlier quoted context omitted.
This error is, in fact, the point. It keeps you from accidentally assigning a normal string to a branded string You have to make a function to apply the brand via a cast, the article explains this as well. function makeObjectId(id: string): ObjectId { return id as ObjectId; }
Ah, yeah the error makes sense. I expected the error, just wanted to understand how Brand was meant to be actually assigned to a primitive. I'm not sure the function is necessary though. This does the same thing const accountId = "125314" as AccountId It makes sense that the technique uses casting.
But what if I instead wrote:
const accountId = AccountId ("125314" );
There would be the needed checks and balances in the function
AccountId(). Wouldn't that do pretty much the same thing?Re: TypeScript: Branded Types
#116Earlier quoted context omitted.
> Anytime I’ve come across the need to do this, I’ve found a class is a better and less complicated solution ... As far as I understand classes are the only real way to get nominal typing in TypeScript. How are classes going to help? As far as I understand TS is structural period. Ex this is valid (!): class Email { constructor(public email: String) {} } let x: Email = new Email('foo@foo.com') let y = { email: '73'}…
I had the same initial reaction, but it turns out that if you have at least one private member, even with the same signature, it works as the parent comment suggests. For example, these two classes are distinct to TypeScript: class Name { constructor(private value: string) {} getValue() { return this.value; } } class Email { constructor(private value: string) {} getValue() { return this.value; } } const email: Email…
Re: TypeScript: Branded Types
#117Anytime I’ve come across the need to do this, I’ve found a class is a better and less complicated solution. I really like the pattern of value objects from Domain Driven Design. Create a class that stores the value, for example email address. In the class constructor, take a string and validate it. Then anywhere that you need a valid email address, have it accept an instance of the Email class. As far as I understand…
> As far as I understand classes are the only real way to get nominal typing in TypeScript. Although classes and instances are ultimately structural, right? let foo = new Foo(); assert(foo.constructor == Foo);
Re: TypeScript: Branded Types
#118Re: TypeScript: Branded Types
#119- currencies
You may have some sort of integer representing the number of cents of some currency and you want to avoid doing operations between the wrong currencies (such as adding euros and pesos).
You can create branded types and functions that work on those Euro branded numbers and then decide how to do math on it. Or numbers representing metals and such.
It's useful in other scenarios such as a, idk, strings, you could theoretically brand strings as idk ASCII or UTF-8 or the content of an http body to avoid mixing those up when encoding but I want to emphasize that often many of those hacks are easier to be handled with stuff like dictionaries or tagged unions.
An example of what can be achieved with similar approaches (beware it's oriented for people that are at least amateur practitioners of functional programming) is Giulio Canti's (an Italian mathematician and previously author of t-comb, fp-ts, io-ts, optic-ts, and now effect and effect/schema), the money-ts library:
Re: TypeScript: Branded Types
#120Earlier quoted context omitted.
Sorry, how do you apply it to branding primitives? The basic const accountId: Brand = "123654" Has the error Type 'string' is not assignable to type '{ readonly __Account_brand: never; }'
This error is, in fact, the point. It keeps you from accidentally assigning a normal string to a branded string You have to make a function to apply the brand via a cast, the article explains this as well. function makeObjectId(id: string): ObjectId { return id as ObjectId; }
Another way you can implement the same: through a class constructor like new UserId('some string') which can also throw or let you handle operations on the class itself while allowing you to get the value.