Live data from Hacker News

Branded types for TypeScript

carlos-menezes.com

101–110 of 152 posts

Re: Branded types for TypeScript

#101

Strings might not be the best way of demonstrating nominal typing, since that's already something TypeScript can manage: https://www.typescriptlang.org/play/?#code/C4TwDgpgBAEghgZwB... Also, since all examples of branded / nominal types in TypeScript use `as` (I assume to get around the fact that the object you're returning isn't actually of the shape you're saying it is...), you should read up on the pitfalls of it:…

I'm legitimately confused about why so many people in this thread are showing off template literal types as if actual hash functions just prepend "hash_" onto strings and call it a day. There are a lot of different types of data that don't have a predictable shape that TLTs just don't help with at all.

While the pitfalls of mindlessly slapping `as XYZ` on lines to make it compile certainly exist (when the type definition changes without the areas with `as` being updated, etc), I don't know if branded values are really the place where they pop up. You brand a primitive when you want to differentiate it from other primitives that otherwise have the same underlying type. In that scenario, you can't really change the definition of the underlying primitive, so you can't really run into issues there.

Re: Branded types for TypeScript

#102
post #89

Earlier quoted context omitted.

It is a form of strong typing because integer could be the length of your toe nail, a temperature or the seconds since the unix epoch. Sometimes you really want to make sure someone is not going to introduce billion dollar bugs, by making the type different from the underlying representation. In Haskell that would be sth like newtype Temperature = Int At other times, you just want to document in stead of forcing sema…

The AgeMin/AgeMax example seems more of a deficiency due to a lack of named function parameters; it would be equally clear if it had a type signature (using OCaml as an example) of val is_admissible : min_age:int -> max_age:int -> bool

There are other places were you use types as well.

Also, if you later decide that an Age should not be int, but a string, you wont miss it in refactoring whereas in your example you don't have that facility.

Re: Branded types for TypeScript

#103
post #71

I like to make the 'brand' property optional so that it doesn't actually have to be there at runtime, but the TypeScript type-checker will still catch mismatches type USDollars = number & { currency?: "USD" } (or something like that; My TypeScript-fu may be slightly rusty at the moment.) Of course a `number` won't have a currency property, but if it did , its value would be "USD"! I've found that TypeScript's structu…

Making it optional doesn't work, the brand property needs to be required. Doesn't mean that you actually have to define the property at runtime, you'd usually cast the number to USDollars where relevant

According to Deno it does work.

  type USD = number & { currency?: "USD" }
  type CAD = number & { currency?: "CAD" }
  const fiveUsd : USD = 5;
  const fiveCad : CAD = fiveUsd;
  
  console.log("How many CAD? " + fiveCad);
Results in an error,

  Type '"USD"' is not assignable to type '"CAD"'.
If by "doesn't work" you mean that the implicit number->USD conversion is allowed, then I disagree with the judgement, as that is by design. But once the values are in variables of more specific types, the type-checker will catch it.

Re: Branded types for TypeScript

#104

Earlier quoted context omitted.

You might need to read the article again, because those things are unrelated. Or you should explain what you mean.

I read the article, thanks. It makes the claims that (1) a string of a specific form (a hash) could be misused (eg. someone might call toUpper on it) or (2) passed in incorrect order to a function that takes multiple strings. Named parameters / outward-facing labels (Swift) completely solves (2). For (1), the solution is just ugly. Just use the type system in a normal manner and make a safe class “Hash” that doesn’t…

[deleted]

Re: Branded types for TypeScript

#105
That's nice but it seems you're in search of a nominal type system within a structurally typed language. I'd posit that it's usually much better to step and try to approach the problem from the way the language lends itself to be solved rather than trying to hack it to fit your expectations

Re: Branded types for TypeScript

#106
post #4

Really ugly way to avoid something already solved by named parameters and/or namespacing

This is more about nominal VS structural typing. I don't see how named parameters or namespacing would prevent accidental structural type matches.

TypeScript is a structurally typed language on purpose. But even then nominal features already exist that would have solved this problem much more elegantly. Such as `unique symbol`

https://www.typescriptlang.org/docs/handbook/symbols.html#un...

Re: Branded types for TypeScript

#107

Is 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 when the developer knows something info about the code that Typescript can't.

Re: Branded types for TypeScript

#108
post #106
post #4

Earlier quoted context omitted.

This is more about nominal VS structural typing. I don't see how named parameters or namespacing would prevent accidental structural type matches.

TypeScript is a structurally typed language on purpose. But even then nominal features already exist that would have solved this problem much more elegantly. Such as `unique symbol` https://www.typescriptlang.org/docs/handbook/symbols.html#un...

Did you... read the article?

Re: Branded types for TypeScript

#109
post #21

It 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 this case where the wrong order of parameters was the issue, you can solve it with Template Literal Types You can solve the issue in this particular example because the "hashing" function happens to just append a prefix to the input. There is a lot of data that isn't shaped in that manner but would be useful to differentiate nonetheless. > And for `hash.toUpperCase()`, it's a valid program. It's odd to try and a…

The real problem is that hashes as strings is wrong.

Hashes are typically numbers.

Do you store people's ages as hex strings?

Re: Branded types for TypeScript

#110
post #105

That's nice but it seems you're in search of a nominal type system within a structurally typed language. I'd posit that it's usually much better to step and try to approach the problem from the way the language lends itself to be solved rather than trying to hack it to fit your expectations

Do you mind elaborating on why this approach would be bad in general? It avoids the overhead of creating new classes and wrapping your objects when all you care about is the type-safety that the class would provide.

How would you "approach the problem from the way the language lends itself to be solved"?

Post reply on HN