In most languages, doing what this article describes is quite straightforward: you would just define a new type (/ struct / class) called ‘Hash’, which functions can take or return. The language automatically treats this as a completely new type. This is called ‘nominal typing’: type equality is based on the name of the type. The complication with TypeScript is that it doesn’t have nominal typing. Instead, it has ‘st…
Branded types for TypeScript
31–40 of 152 posts
Re: Branded types for TypeScript
#32Earlier quoted context omitted.
I see where you are coming from but you are not quite understanding what the OP was saying class A { public value: number } class B { public value: number } const x: A = new B() // no error This is structural typing (shape defines type), if typescript had nominal typing (name defines type) this would give an error. You could brand these classes to forcefully cause this to error. Branding makes structural typing work…
Indeed, this is what I was trying to say!
"structural typing" and "nominal typing" are still quite new terms for most devs
Re: Branded types for TypeScript
#33Equality can be problematic too. Imagine an Extension type, one could compare it with ".mp4" or "mp4", which one is correct?
Opaque types (that extend from `unknown` instead of T) work around these problems by forcing users through selector functions.
Re: Branded types for TypeScript
#34Earlier quoted context omitted.
I see where you are coming from but you are not quite understanding what the OP was saying class A { public value: number } class B { public value: number } const x: A = new B() // no error This is structural typing (shape defines type), if typescript had nominal typing (name defines type) this would give an error. You could brand these classes to forcefully cause this to error. Branding makes structural typing work…
You can fix that fairly easily using private variables: class A { private value: number } class B { private value: number } const x: A = new B() // error You can also use the new Javascript private syntax (`#value`). And you can still have public values that are the same, so if you want to force a particular class to have nominal typing, you can add an unused private variable to the class, something like `private __f…
Re: Branded types for TypeScript
#35Earlier quoted context omitted.
I see where you are coming from but you are not quite understanding what the OP was saying class A { public value: number } class B { public value: number } const x: A = new B() // no error This is structural typing (shape defines type), if typescript had nominal typing (name defines type) this would give an error. You could brand these classes to forcefully cause this to error. Branding makes structural typing work…
The article describes making "number" a different type, not A and B. It's true that making A and B different is a unique problem of TypeScript, but making number a different type is a common issue in many languages.
Some languages all values are objects and in those languages then the branding argument applies the same way. For languages with nominal typing and primitives you need to box the type yes. Regardless the core of the issue is understanding how structural typing works vs nominal typing
Re: Branded types for TypeScript
#36Re: Branded types for TypeScript
#37The main consequence is that we need an extra level of vigilance and discipline in PR reviews, or else implicit trust in one another. With a small team, this isn't difficult to maintain, even if it means that typing isn't 100% perfect in our codebase.
I've seen two implementations of branded types. One of them exploits a quirk with `never` and seems like a dirty hack that might no longer work in a future TS release. The other implementation is detailed in this article, and requires the addition of unique field value to objects. In my opinion, this pollutes your model in the same way that a TS tagged union does, and it's not worth the trade-off.
When TypeScript natively supports discriminated unions and (optional!) nominal typing, I will be overjoyed.
Re: Branded types for TypeScript
#38It 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…
This isn't valuable to you? How do you get this without nominal typing, especially of primatives?
Re: Branded types for TypeScript
#39It 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…
How do you do this with template literal types? Does that mean you changed the string that gets passed at runtime? The nice thing about branding (or the "flavored" variant which is weaker but more convenient) is that it's just a type check and nothing changes at runtime.
I wonder if a more natural solution would be to extend the String class and use that to wrap/guard things:
class Hash extends String {}
compareHash(hash: Hash, input: string)
Here's an example: https://www.typescriptlang.org/play/?#code/MYGwhgzhAEASkAtoF...