Live data from Hacker News

TypeScript: Branded Types

prosopo.io

21–30 of 209 posts

Re: TypeScript: Branded Types

#21
post #2

Amazing, 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?

I think the example could be better in this article. Let's say you have a function that takes a database id, an email address, and a name. They're all strings. If you pass arguments to this function and you mess up the order for some reason then the compiler has no idea. Hopefully your unit tests catch this but it won't be as obvious. Branded types solve this problem because you can't pass a name string as an argumen…

That’s just plain encapsulation, if I understand you correctly. Branding, on the other hand, prevents complex types from being confused.

Re: TypeScript: Branded Types

#23

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…

[deleted]

Re: TypeScript: Branded Types

#24

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…

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; }'

Re: TypeScript: Branded Types

#25
post #24

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…

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;
    }

Re: TypeScript: Branded Types

#26
post #24

Earlier 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; }

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.

Re: TypeScript: Branded Types

#27

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…

Yeah in TS' own playground example they don't create a Symbol, they just intersect it inline: https://www.typescriptlang.org/play#example/nominal-typing

Re: TypeScript: Branded Types

#28
post #19

Earlier quoted context omitted.

Yes you can, it works just the same :-)

Sorry, with the Symbol? How does it work?

You don't even need the symbol. If you want the simplest thing that will work:

    type Velocity = number & { BRAND: "Velocity" }
    type Distance = number & { BRAND: "Distance" }

    var x = 100 as Distance

Re: TypeScript: Branded Types

#29
post #2

Amazing, 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 don't use them but your favorite libraries do.

This is very accurate, in particular when the output type of something depends on the input type.

Data validation, typed database access, or functional programming libraries are good examples. Particularly the modern, leading libraries of such areas, if you look into their code you'll generally see very intricate typing. For FP libraries it's particularly tough. I like to use Remeda which emphasizes being very type-safe, but that means it's inherently more limited in what functions it can offer compare to other libraries which choose to compromise their type-safety. These kinds of techniques mean that libraries can offer greater functionality while remaining type-safe.

Re: TypeScript: Branded Types

#30
It's a clever trick, but the compiler errors leave a lot to be desired. If a TS library makes heavy use of nominal (branded/distinct) types in a domain where accidentally passing values of the wrong type is common, I can imagine a lot of library users being more confused, not less, by code that uses this approach.

The article reads more like an endorsement of languages that do structurally-aware nominal typing (that is, languages that are nominally typed but that have awareness of structural equivalence so that zero-cost conversions and intelligible compile-time errors for invalid conversions are first class) than a persuasive case for the symbol-smuggling trick described.

Post reply on HN