5 ms·
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 typ
by pprotas 2y ago
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?
- skybrian 2y agoI use branded types for database id’s to avoid mixing them up. It seems like better documentation and might catch some errors, but mixing up database id’s seems like an uncommon error, so hard to say.
- odyssey7 2y agoNope, if you have lower expectations for your type system and are using TS then you are properly where you want to be.
- shadowgovt 2y agoProbably not. I could definitely see using something like this to force some constraints on my favorite bugbear for my problem domain: values that should have units. It matters a lot if "time" is nanoseconds, microseconds, or seconds, but most time-related functions just take in "number" like that's okay and won't cause thousand- or million-fold magnitude errors. This is one way to provide some language safeguarding against treating 5 nanoseconds as the same as 5 microseconds.
- chowells 2y agoYou've never had values that are only valid within a specific context and wanted to prevent using them incorrectly?
- IshKebab 2y agoYeah structural typing assumes that all fields with the same name & type mean the same thing, but that clearly isn't always the case.
- brigadier132 2y agoYou don't use them but your favorite libraries do.
- maeil 2y agoThis 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.
- JJMalina 2y agoI 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 argument that is expected to be an email address. 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.
- LadyCailin 2y agoThat’s just plain encapsulation, if I understand you correctly. Branding, on the other hand, prevents complex types from being confused.
- mason55 2y agoNot really, not for TS at least. If you just want to take a string and call it an email address and have your function only accept email addresses (the simplest use case) then you need to use branding. That's not encapsulation.
- antifa 2y agoOr double your GC burden by using a wrapper class.
- ebolyen 2y agoBranding works on primitive types as well, which is I think the most interesting use case. I would also agree that it's harder to confuse complex types as any single instance of a type is unlikely to overlap once you have a few fields.
- contextnavidad 2y ago
- amatecha 2y agoYeah no, IMO this seems totally extraneous and a layer of complexity not worth introducing to any project. I've never encountered a case where I care that I need to differentiate between two types of exactly the same structure, and my gut feeling (without actually prototyping out an example) is that if this actually makes a difference in your code, you should probably be making the differentiation further up in the flow of information...
- eyelidlessness 2y agoNot necessarily wrong. You’re probably doing the same work at runtime. That might be either just as good (a subjective preference) or more correct (for certain kinds of dynamism) depending on context. In some cases, there are clear static invariants that could be guaranteed at compile time, and some folks will be more inclined to squeeze as much out of that as possible. In my experience, the benefits of that vary wildly from “total waste of time” to “solves a recurring and expensive problem for good”, with a lot in between.
- CapsAdmin 2y ago> The need for tricks like this (and other complicated types) is simply not there. It just depends on how constrained by types you want your code to be and how much time and effort you're willing to spend maintaining and writing code that fits within those constraints. Sometimes complicated types are introduced because you want to maintain editor features such as find by reference and the ability to refactor later. When it works, removing or adding new features feels fast, easy and safe. In the articles case you could differentiate between an email string type and a user id string type. Maybe sometime in the future you want to change the id to an integer instead, so now that it's already distinguished you could find all those places where that's applied. That's at least a selling point, in practice I've used this a few times but it doesn't come up that often. Sometimes the blast radius of a type isn't big so it's not worth doing.