I can see this being useful and it seems about as neat a solution as you can currently get in TypeScript as it stands today, but it’s still cumbersome. My feeling is that while you can do this, you’re swimming upstream as the language is working against you. Reaching for such a solution should probably be avoided as much as possible.
I don't see why. I greatly prefer typescript's structural typing for almost everything. But id's in data models are an exception, so I use branding for those. It works perfectly, the only overhead is in the write-once declaration and now I am protected from accidentally using an AccountId where a MemberId was expected, even though they are both just strings.
TypeScript: Branded Types
11–20 of 209 posts
Re: TypeScript: Branded Types
#12Earlier quoted context omitted.
I don't see why. I greatly prefer typescript's structural typing for almost everything. But id's in data models are an exception, so I use branding for those. It works perfectly, the only overhead is in the write-once declaration and now I am protected from accidentally using an AccountId where a MemberId was expected, even though they are both just strings.
Yes! I've wanted this in Typescript for ages! Is it possible to brand primitives though?
Re: TypeScript: Branded Types
#13Amazing, we have invented nominal typing in a structural typing system. Sometimes I wonder what kind of programs are written using all these complicated TS types. Anecdotally, we use very simple, basic types in our codebase. The need for tricks like this (and other complicated types) is simply not there. Are we doing something wrong?
If you argue that this is not a common problem in practice, then I tend to agree. I haven't seen code with mixed up arguments very much, but when it does happen it can have bad consequences. IMO there is not a big cost to pay to have this extra safety. In TypeScript it's ugly, but in other languages that natively support this typing like Scala, Rust, Swift and Haskell, it works nicely.
Re: TypeScript: Branded Types
#14Re: TypeScript: Branded Types
#15You 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 enough that I have a little helper for it:
/**
* Brands a type by intersecting it with a type with a brand property based on
* the provided brand string.
*/
export type Brand = T & {
readonly [B in Brand as `__${B}_brand`]: never;
};
Use it like: type ObjectId = Brand;
And the hover-over type is: type ObjectId = string & {
readonly __ObjectId_brand: never;
}Re: TypeScript: Branded Types
#16Amazing, we have invented nominal typing in a structural typing system. Sometimes I wonder what kind of programs are written using all these complicated TS types. Anecdotally, we use very simple, basic types in our codebase. The need for tricks like this (and other complicated types) is simply not there. Are we doing something wrong?
You've never had values that are only valid within a specific context and wanted to prevent using them incorrectly?
Re: TypeScript: Branded Types
#17Amazing, we have invented nominal typing in a structural typing system. Sometimes I wonder what kind of programs are written using all these complicated TS types. Anecdotally, we use very simple, basic types in our codebase. The need for tricks like this (and other complicated types) is simply not there. Are we doing something wrong?
Re: TypeScript: Branded Types
#18Re: TypeScript: Branded Types
#19Re: TypeScript: Branded Types
#20I can see this being useful and it seems about as neat a solution as you can currently get in TypeScript as it stands today, but it’s still cumbersome. My feeling is that while you can do this, you’re swimming upstream as the language is working against you. Reaching for such a solution should probably be avoided as much as possible.
I don't see why. I greatly prefer typescript's structural typing for almost everything. But id's in data models are an exception, so I use branding for those. It works perfectly, the only overhead is in the write-once declaration and now I am protected from accidentally using an AccountId where a MemberId was expected, even though they are both just strings.