TE
TechEcho
Home24h TopNewestBestAskShowJobs
GitHubTwitter
Home

TechEcho

A tech news platform built with Next.js, providing global tech news and discussions.

GitHubTwitter

Home

HomeNewestBestAskShowJobs

Resources

HackerNews APIOriginal HackerNewsNext.js

© 2025 TechEcho. All rights reserved.

TypeScript: Branded Types

190 pointsby arbolabout 1 year ago

28 comments

spankaleeabout 1 year ago
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.<p>You really don&#x27;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.<p>I use branding enough that I have a little helper for it:<p><pre><code> &#x2F;** * Brands a type by intersecting it with a type with a brand property based on * the provided brand string. *&#x2F; export type Brand&lt;T, Brand extends string&gt; = T &amp; { readonly [B in Brand as `__${B}_brand`]: never; }; </code></pre> Use it like:<p><pre><code> type ObjectId = Brand&lt;string, &#x27;ObjectId&#x27;&gt;; </code></pre> And the hover-over type is:<p><pre><code> type ObjectId = string &amp; { readonly __ObjectId_brand: never; }</code></pre>
评论 #40147892 未加载
评论 #40148018 未加载
评论 #40147869 未加载
评论 #40148289 未加载
评论 #40153282 未加载
thepaulmcbrideabout 1 year ago
Anytime I’ve come across the need to do this, I’ve found a class is a better and less complicated solution.<p>I really like the pattern of value objects from Domain Driven Design. Create a class that stores the value, for example email address.<p>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.<p>As far as I understand classes are the only real way to get nominal typing in TypeScript.
评论 #40149903 未加载
评论 #40151773 未加载
评论 #40150684 未加载
评论 #40150732 未加载
评论 #40150030 未加载
epolanskiabout 1 year ago
An example of what you can implement on top of branded types that I want to share with fellow hackers:<p>- currencies<p>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).<p>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.<p>It&#x27;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 <i>easier</i> to be handled with stuff like dictionaries or tagged unions.<p>An example of what can be achieved with similar approaches (beware it&#x27;s oriented for people that are at least amateur practitioners of functional programming) is Giulio Canti&#x27;s (an Italian mathematician and previously author of t-comb, fp-ts, io-ts, optic-ts, and now effect and effect&#x2F;schema), the money-ts library:<p><a href="https:&#x2F;&#x2F;github.com&#x2F;gcanti&#x2F;money-ts">https:&#x2F;&#x2F;github.com&#x2F;gcanti&#x2F;money-ts</a>
评论 #40152689 未加载
评论 #40153837 未加载
评论 #40152258 未加载
评论 #40165665 未加载
jonathanlydallabout 1 year ago
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.<p>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.
评论 #40147626 未加载
评论 #40147579 未加载
评论 #40148232 未加载
munk-aabout 1 year ago
Isn&#x27;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 compatibility needs to be explicitly declared - if your array is iterable defining that it implements iterable lets your compiler shout at you if iterable suddenly gets another abstract method that you didn&#x27;t implement - and it makes sure that if you add a method `current` to a class it doesn&#x27;t suddenly means that it properly supports iterable-ity.<p>Determining types by what things appear to do instead of what they state they do seems like a generally unnecessary compromise in typing safety that isn&#x27;t really worth the minuscule amount of effort it can save.
评论 #40153141 未加载
评论 #40151694 未加载
评论 #40152032 未加载
JJMalinaabout 1 year ago
Zod has branded types: <a href="https:&#x2F;&#x2F;zod.dev&#x2F;?id=brand" rel="nofollow">https:&#x2F;&#x2F;zod.dev&#x2F;?id=brand</a> it works really nicely with Zod schemas
dvtabout 1 year ago
Weird idea, as types in TS are structural by <i>design</i>. If this is something you need, it smells like &quot;runtime checking&quot; not amending the type system.
评论 #40149382 未加载
评论 #40152520 未加载
评论 #40150099 未加载
评论 #40149767 未加载
Slixabout 1 year ago
This is also useful for having a type that needs to be verified in some way before being used or trusted. UnverifiedLoginCookie vs. VerifiedLoginCookie
zbentleyabout 1 year ago
It&#x27;s a clever trick, but the compiler errors leave a lot to be desired. If a TS library makes heavy use of nominal (branded&#x2F;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.<p>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.
评论 #40148199 未加载
评论 #40148272 未加载
评论 #40148186 未加载
techn00about 1 year ago
Very useful for DDD, like having an Email type, or String100 (string of 100 characters)
评论 #40148200 未加载
hankchinaskiabout 1 year ago
Maybe is me but this seems way overengineered
评论 #40164354 未加载
herpdyderpabout 1 year ago
The implementation for `RemoveBrand` is incorrect: it currently grabs all property types of `T`, it&#x27;s not removing the property `[brand]` from `T`. It should be `Omit&lt;T, typeof brand&gt;`
dimitrisnlabout 1 year ago
Selfish plug about the same topic <a href="https:&#x2F;&#x2F;dnlytras.com&#x2F;blog&#x2F;nominal-types" rel="nofollow">https:&#x2F;&#x2F;dnlytras.com&#x2F;blog&#x2F;nominal-types</a>
评论 #40162629 未加载
leecommamichaelabout 1 year ago
The Odin language has a ‘distinct’ type-qualifier which accomplishes this tersely. It’s a big part of the type system, and C’s type-system for that matter.
bedersabout 1 year ago
Ah, the magical disappearing type system - now being used for nominal typing.<p>I&#x27;m curious to see what the JS code looks like for casts and type checks in that case.
评论 #40149364 未加载
评论 #40149404 未加载
aleksiy123about 1 year ago
Anyone else sometimes get more sucked in to perfectly typing stuff instead of writing code.<p>I guess working in the pure logic and formalism of types is just addictive.<p>I love it.
评论 #40151383 未加载
gmdrdabout 1 year ago
Is there something wrong with creating opaque types using this method?<p><pre><code> type ObjectId = string &amp; { readonly __tag: unique symbol } </code></pre> This way, we don&#x27;t need to use `never`, but we still prevent the creation of a structurally equivalent type by mistake.
satvikpendemabout 1 year ago
Reminds me of another sort of type-driven development, making invalid states unrepresentable: <a href="https:&#x2F;&#x2F;geeklaunch.io&#x2F;blog&#x2F;make-invalid-states-unrepresentable&#x2F;" rel="nofollow">https:&#x2F;&#x2F;geeklaunch.io&#x2F;blog&#x2F;make-invalid-states-unrepresentab...</a>
SergeAxabout 1 year ago
Seems like patching earlier language architecture mistake. If you create custom types, you should not use duck typing on them. You should use typecasting, so the programmer would write something like `result = processEntityA(A(B))`
rowanG077about 1 year ago
How do you ensure someone doesn&#x27;t reuse a brand string? Seems pretty brittle to me.
black_puppydogabout 1 year ago
Structural typing, and especially for primitives, with these work-arounds being so clunky and not built into the standard library, was <i>the</i> reason I started picking up Rust...
erik_seabergabout 1 year ago
This works because casts are allowed to quietly create values whose types are wrong. It would have been better if the cast added a runtime check, or at least we distinguish sound (checked) and unsound casts the way C++ does.<p>I think Haskell avoid this by actually requiring you to write sound conversion functions between phantom types (it helps that phantom types don&#x27;t involve any visible state at runtime).
评论 #40149372 未加载
lang_agnosticabout 1 year ago
Isn&#x27;t this just phantom types in other programming languages?
评论 #40150584 未加载
bazoom42about 1 year ago
I think Typescript need nominal type aliases for primitives.
klysmabout 1 year ago
The downside of structural typing
jibbitabout 1 year ago
or a tagged type if you&#x27;re old enough to have ever been exposed to another language
mirekrusinabout 1 year ago
Flow is much better with opaque types.<p>Also nominal types for classes.<p>And correct variance.<p>And adhering to liskov substitution principles.<p>And exact object types.<p>And spread on types matching runtime behavior.<p>And proper no transpilation mode with full access to the language.<p>And has 10x less LoC than ts.<p>ps. before somebody says &quot;flow is dead&quot; have a look at flow contributions [0] vs typescript contributions [1]<p>[0] <a href="https:&#x2F;&#x2F;github.com&#x2F;facebook&#x2F;flow&#x2F;graphs&#x2F;contributors">https:&#x2F;&#x2F;github.com&#x2F;facebook&#x2F;flow&#x2F;graphs&#x2F;contributors</a><p>[1] <a href="https:&#x2F;&#x2F;github.com&#x2F;microsoft&#x2F;TypeScript&#x2F;graphs&#x2F;contributors">https:&#x2F;&#x2F;github.com&#x2F;microsoft&#x2F;TypeScript&#x2F;graphs&#x2F;contributors</a>
评论 #40148538 未加载
评论 #40148778 未加载
评论 #40148326 未加载
评论 #40148218 未加载
评论 #40153787 未加载
评论 #40150148 未加载
评论 #40148961 未加载
pprotasabout 1 year ago
Amazing, we have invented nominal typing in a structural typing system.<p>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?
评论 #40147675 未加载
评论 #40147617 未加载
评论 #40147591 未加载
评论 #40147635 未加载
评论 #40148337 未加载
评论 #40147603 未加载
评论 #40153640 未加载
评论 #40147551 未加载
评论 #40147751 未加载