Earlier quoted context omitted.
> Classes exist at runtime Not necessarily, depending on the language. Functional languages and system languages such as OCaml, Haskell, Rust, but also C (painfully) and C++ can represent wrapper types within a nominal type system at no runtime cost.
Haskell implements type classes via dictionary passing that don’t always get optimized away by the compiler so it does have a slight runtime cost: https://okmij.org/ftp/Computation/typeclass.html#dict In Rust, using trait objects also generates a vtable for dynamic dispatch so in that case traits are not fully erased: https://web.mit.edu/rust-lang_v1.25/arch/amd64_ubuntu1404/sh...
Branded types for TypeScript
131–140 of 152 posts
Re: Branded types for TypeScript
#132It took me so long to fully appreciate TypeScript's design decision for doing structural typing vs. nominal typing. In all scenarios, including the "issue" highlighted in this article there is no reason for wanting nominal typing. In this case where the wrong order of parameters was the issue, you can solve it with [Template Literal Types]( https://www.typescriptlang.org/docs/handbook/2/template-lite... ). See [1]. A…
One tricky scenario I stumbled on is `.toString()`. Everything has a `.toString()` but some objects A have `.toString(arg1, arg2, arg3)`. But replacing A with something that does not have toString with arguments still type checks, yet will probably result in serious error.
I know it's very tempting for people coming from OOP languages to use their own custom toString functions, but you really shouldn't. If you really need a string version of an object for debugging purposes you should instead get it through JSON.stringify. This is partly because .toString() isn't really meant to be used by you in JS. You can, and in a few cases it may make sense, but it's usually unnecessary because JS will do it automatically if you simply wrap your non-string primitives in the string where you want to use them.
In general it's better to work with objects directly and not think of them as "classes". I think (and this is my opinion which people are going to disagree with) in general you're far better off by very rarely using classes at all in TS. There are obviously edge cases where you're going to need classes, but for 95% of your code they are going to be very unnecessary and often make it much harder for developers who may not primarily work with JS or another weakly typed language. Part of this is because classes aren't actually classes, but mainly it's because you can almost always achieve what you want with an interface or even a Type in a manner that is usually more efficient, more maintainable and easier to test because of it's decoupled nature. I have this opinion after working with JS in both the back-end and front-end for over a decade and seeing how horrible things can go wrong because we all write shitty code on a thursday afternoon, and because JS often won't work like many people from C#, Java or similar backgrounds might expect.
Re: Branded types for TypeScript
#133I ran the branded type example listed in the blog through bun and it ran without issuing a warning or error for the "This won't compile!" code. Is there any way to get bun to be strict for TypeScript errors? Does deno catch this TS bug?
To do type-checking you need to run TSC.
Re: Branded types for TypeScript
#134Earlier quoted context omitted.
> which are true nominal typing. One part that was not clear to me without testing, and since I do not use typescript regularly, was that you only get nominal typing between the classes that share the private member and if you start going out side that set you lose nominal typing. So you do not get a nominal type, but you can get a subset of types that when interacting with each other act as if they were nominal type…
Without your example, I would've bet that TS uses structural typing for interfaces and nominal typing for classes.
Re: Branded types for TypeScript
#135If I have a function written in TS which takes a string type parameter called a hash... isn't it already obvious what the function wants?
Furthermore when the function in question is a hash checking function, it is working exactly as intended when it returns something like "invalid hash" when there is a problem with the hash. You either supply it a valid well formed hash and the function returns success, or you supply it any kind of non-valid hash and the function returns a failure. What is the problem?
In case the function is not a hash checking function per se, but e.g. a function which uses the hash for someyhing, you will still need to perform some checks on the hash before using it. Or it could be a valid hash, but there is nothing to be found with that hash, in which case once again everything already works exactly as it should and you get nothing back.
It's like having a function checkBallColor which wants you to supply a "ball" to it. Why would you need to explicitly define that you need to give it a ball with a color property in which the color is specified in a certain way? If someone calls that function with a ball that has a malformed color property, then the function simply returns that the ball's color property is malformed. You will, in most cases probably, have to check the color property in runtime anyway.
I've used TS based graphics libraries and they often come with something like setColorHex, setColorRGB, etc. functions so that you know how the color should be given. If you supply the color in a wrong way, nothing happens and I think that is just fine.
Sorry for the rant, but I just don't get some of these problems. Like... you either supply a valid hash and all is fine, or you don't and then you simply figure out why isn't it valid, which you will have to do with this branding system as well.
Re: Branded types for TypeScript
#136I ran the branded type example listed in the blog through bun and it ran without issuing a warning or error for the "This won't compile!" code. Is there any way to get bun to be strict for TypeScript errors? Does deno catch this TS bug?
No, neither Deno nor Bun does any type-checking. They both just convert the code to JS. To do type-checking you need to run TSC.
Re: Branded types for TypeScript
#137It took me so long to fully appreciate TypeScript's design decision for doing structural typing vs. nominal typing. In all scenarios, including the "issue" highlighted in this article there is no reason for wanting nominal typing. In this case where the wrong order of parameters was the issue, you can solve it with [Template Literal Types]( https://www.typescriptlang.org/docs/handbook/2/template-lite... ). See [1]. A…
> In all scenarios [...] there is no reason for wanting nominal typing. Hard disagree. It's very useful to e.g. make a `PasswordResetToken` be different from a `CsrfToken`. Prepending a template literal changes the underlying value and you can no longer do stuff like `Buffer.from(token, 'base64')`. It's just a poor-man's version of branding with all the disadvantages and none of the advantages. You can still `hash.to…
Re: Branded types for TypeScript
#138It’s trivial to do with nominal type. In Python: ```Hash = NewType('Hash', str) def generate_hash(what: str) -> Hash: return Hash(f“hashed_{what}”)```
Re: Branded types for TypeScript
#139Is it erased at runtime? The article doesn’t mention this.
It is "erased" in this example in the sense that the hashes are just strings at runtime, and not other objects instead. That's because the `generateHash` function in the article's example uses `as Hash` to tell the compiler "yes, I know I'm only returning a string here, but trust me, this is actually a `string & { [__brand]: 'Hash' }`". That `as Hash` is known as a type assertion in Typescript and is normally used wh…
At first it looks like you could just lie and say `x as User` or something but it's not the case.