TE
科技回声
首页24小时热榜最新最佳问答展示工作
GitHubTwitter
首页

科技回声

基于 Next.js 构建的科技新闻平台,提供全球科技新闻和讨论内容。

GitHubTwitter

首页

首页最新最佳问答展示工作

资源链接

HackerNews API原版 HackerNewsNext.js

© 2025 科技回声. 版权所有。

TypeScript: Branded Types

190 点作者 arbol大约 1 年前

28 条评论

spankalee大约 1 年前
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 未加载
thepaulmcbride大约 1 年前
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 未加载
epolanski大约 1 年前
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 未加载
jonathanlydall大约 1 年前
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-a大约 1 年前
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 未加载
JJMalina大约 1 年前
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
dvt大约 1 年前
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 未加载
Slix大约 1 年前
This is also useful for having a type that needs to be verified in some way before being used or trusted. UnverifiedLoginCookie vs. VerifiedLoginCookie
zbentley大约 1 年前
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 未加载
techn00大约 1 年前
Very useful for DDD, like having an Email type, or String100 (string of 100 characters)
评论 #40148200 未加载
hankchinaski大约 1 年前
Maybe is me but this seems way overengineered
评论 #40164354 未加载
herpdyderp大约 1 年前
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;`
dimitrisnl大约 1 年前
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 未加载
leecommamichael大约 1 年前
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.
beders大约 1 年前
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 未加载
aleksiy123大约 1 年前
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 未加载
gmdrd大约 1 年前
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.
satvikpendem大约 1 年前
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>
SergeAx大约 1 年前
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))`
rowanG077大约 1 年前
How do you ensure someone doesn&#x27;t reuse a brand string? Seems pretty brittle to me.
black_puppydog大约 1 年前
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_seaberg大约 1 年前
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_agnostic大约 1 年前
Isn&#x27;t this just phantom types in other programming languages?
评论 #40150584 未加载
bazoom42大约 1 年前
I think Typescript need nominal type aliases for primitives.
klysm大约 1 年前
The downside of structural typing
jibbit大约 1 年前
or a tagged type if you&#x27;re old enough to have ever been exposed to another language
mirekrusin大约 1 年前
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 未加载
pprotas大约 1 年前
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 未加载