Live data from Hacker News

TypeScript: Branded Types

prosopo.io

151–160 of 209 posts

Re: TypeScript: Branded Types

#151
post #141

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…

Is all Typescript unreadable, or is that just your style?

Are all of your comments devoid of content, or just this one?

Re: TypeScript: Branded Types

#152
post #136

Earlier quoted context omitted.

That's a very clear example. 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?

I personally prefer less code and more explicit casting

  const accountId = "125314" as AccountId;
or

  const accountId = new AccountId("125314");
or

  const accountId = accountIdFromString("125314");
is all "explicit" casting in my understanding, one way or the other, and the first ... as ... being on the lowest level.

I'd rather go for something like "do the casting close to the source, and in general use 'parse, don’t validate'[0] when getting input from the outer world".

[0]: https://lexi-lambda.github.io/blog/2019/11/05/parse-don-t-va...

Re: TypeScript: Branded Types

#154
post #136

Earlier quoted context omitted.

I personally prefer less code and more explicit casting

const accountId = "125314" as AccountId; or const accountId = new AccountId("125314"); or const accountId = accountIdFromString("125314"); is all "explicit" casting in my understanding, one way or the other, and the first ... as ... being on the lowest level. I'd rather go for something like "do the casting close to the source, and in general use 'parse, don’t validate'[0] when getting input from the outer world". [0…

Agree. I was talking about

    const accountId = AccountId (“43525”)
being less explicit

Re: TypeScript: Branded Types

#155

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

I don’t quite see why you’d need to brand simple objects at all! Just give them meaningful field names.

Branding primitives is useful because you don’t otherwise have a name that you can control (e.g. feet versus meters as was suggested elsewhere in this thread, both of which would have type “number”).

The article starts with the example of two different object types with field “x: number”. In that situation you should be branding the field, not the object! If the field has the same name and the same branded type, structural typing is correct for that object shape.

Re: TypeScript: Branded Types

#156
post #136

Earlier quoted context omitted.

I personally prefer less code and more explicit casting

const accountId = "125314" as AccountId; or const accountId = new AccountId("125314"); or const accountId = accountIdFromString("125314"); is all "explicit" casting in my understanding, one way or the other, and the first ... as ... being on the lowest level. I'd rather go for something like "do the casting close to the source, and in general use 'parse, don’t validate'[0] when getting input from the outer world". [0…

The as also does not burden the reader with guessing about the absence of unexpected stuff happening in the functions. Don't pepper the code with mostly unused hidey-holes just to perhaps save some busywork later.

If you do add valuation, make it an honest asValidatedAccountId("123"), not an accountIdFromString("123") that may or may not do more than casting.

(PS, very much off topic: speaking of hidey-holes, are any of the AOP monstrosities still operational?)

Re: TypeScript: Branded Types

#157
post #109

Isn't this just the classic issue of inferred typing coming back to bite us in the way everyone originally predicted? Go runs into the same issue where wildly different types may be considered the same based purely on coincidental naming and matching against interfaces the original authors had no intent to match against. At the end of the day I think the easier system to work with is one in which all type compatibili…

I haven’t run into interfaces coincidentally matching in Go. Have you? It might happen more easily for primitive types, though, and Go does have a way to declare new types: type UserId string

Are these simple aliases or nominally distinct types?

Re: TypeScript: Branded Types

#158
post #83

Earlier quoted context omitted.

My point is that the API consumer does not guarantee types (say it's REST or something), so the assumption that the string you send it will always be the right type (or call it "format") seems like a bad one. Unless you control API usage from end-to-end (which kind of defeats the point of an API), you need error checking (or at least exceptions to bubble up). A lot of times branding is used to mark data received from…

> My point is that the API consumer does not guarantee types (say it's REST or something), so the assumption that the string you send it will always be the right type (or call it "format") seems like a bad one. The raw data from the API will not have any of your internal types applied to it yet, it'll be raw bytes or typed `string`. So I don't really see the connection between this and "I will still always be able to…

> And once your own trusted code has made an OrgId, you don't need any runtime checking to see if it actually is an OrgId.

Right, and once I have a verified OrgId, I'll just keep using the `myOrgId` variable throughout my code, and I don't really need branding. Maybe I can do type aliasing to make the code easier to read (type OrgId = string), but hardline type verification via branding seems moot unless you can make strong runtime guarantees. I mean, don't get me wrong, I think it's a cute novelty, but it doesn't really do anything.

> But a new string will have a type like `string`, not `OrgId`, and then using it as an OrgId won't compile.

Exactly. Maybe I'm wrong, but in a real codebase, I bet branding would probably just confuse people. "Why can't I change the last number of an OrgId?"—well, you see, once you do that, you lose the brand so now you need to manually do `as OrgId`.

Post reply on HN