by arbol on 4/24/24, 4:48 PM with 203 comments
by spankalee on 4/24/24, 6:06 PM
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 enough that I have a little helper for it:
/**
* Brands a type by intersecting it with a type with a brand property based on
* the provided brand string.
*/
export type Brand<T, Brand extends string> = T & {
readonly [B in Brand as `__${B}_brand`]: never;
};
Use it like: type ObjectId = Brand<string, 'ObjectId'>;
And the hover-over type is: type ObjectId = string & {
readonly __ObjectId_brand: never;
}
by thepaulmcbride on 4/24/24, 9:19 PM
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 classes are the only real way to get nominal typing in TypeScript.
by epolanski on 4/24/24, 11:33 PM
- currencies
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).
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.
It'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 easier to be handled with stuff like dictionaries or tagged unions.
An example of what can be achieved with similar approaches (beware it's oriented for people that are at least amateur practitioners of functional programming) is Giulio Canti's (an Italian mathematician and previously author of t-comb, fp-ts, io-ts, optic-ts, and now effect and effect/schema), the money-ts library:
by jonathanlydall on 4/24/24, 5:51 PM
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.
by munk-a on 4/24/24, 10:51 PM
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't really worth the minuscule amount of effort it can save.
by JJMalina on 4/24/24, 6:06 PM
by dvt on 4/24/24, 7:20 PM
by Slix on 4/24/24, 9:00 PM
by zbentley on 4/24/24, 6:51 PM
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.
by techn00 on 4/24/24, 6:24 PM
by hankchinaski on 4/24/24, 11:25 PM
by herpdyderp on 4/24/24, 10:45 PM
by dimitrisnl on 4/24/24, 7:07 PM
by leecommamichael on 4/24/24, 6:15 PM
by beders on 4/24/24, 8:40 PM
I'm curious to see what the JS code looks like for casts and type checks in that case.
by aleksiy123 on 4/24/24, 11:42 PM
I guess working in the pure logic and formalism of types is just addictive.
I love it.
by gmdrd on 4/26/24, 11:33 AM
type ObjectId = string & {
readonly __tag: unique
symbol }
This way, we don't need to use `never`, but we still prevent the creation of a structurally equivalent type by mistake.by satvikpendem on 4/24/24, 9:50 PM
by SergeAx on 4/25/24, 3:41 PM
by rowanG077 on 4/25/24, 1:15 AM
by black_puppydog on 4/25/24, 7:23 AM
by erik_seaberg on 4/24/24, 8:29 PM
I think Haskell avoid this by actually requiring you to write sound conversion functions between phantom types (it helps that phantom types don't involve any visible state at runtime).
by lang_agnostic on 4/24/24, 9:02 PM
by bazoom42 on 4/24/24, 9:38 PM
by klysm on 4/24/24, 7:04 PM
by jibbit on 4/24/24, 6:55 PM
by mirekrusin on 4/24/24, 6:59 PM
Also nominal types for classes.
And correct variance.
And adhering to liskov substitution principles.
And exact object types.
And spread on types matching runtime behavior.
And proper no transpilation mode with full access to the language.
And has 10x less LoC than ts.
ps. before somebody says "flow is dead" have a look at flow contributions [0] vs typescript contributions [1]
[0] https://github.com/facebook/flow/graphs/contributors
[1] https://github.com/microsoft/TypeScript/graphs/contributors
by pprotas on 4/24/24, 5:48 PM
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?