
Opaque Types in Flow and TypeScript - phpnode
https://codemix.com/opaque-types-in-javascript
======
wakeywakeywakey
In TypeScript, you can also use an empty const enum to simulate nominal typing
for opaque types. It gets inlined/removed during compilation:

    
    
      const enum _A = {};
      type A = number & _A;
    

This comes in handy for IDs and other primitives shared by multiple complex
types.

~~~
koblas
I would love to see an extended example. When I tried this in typescript 2.6
it still made PaymentAmount and AccountNumber equivalent types.

~~~
justhelpingout
You can certainly do this - you just need something to contain the enum since
an enum is a nominal subtype of `number` already, so TypeScript unifies
`number & anyEnum` to `number`.

The best tagging mechanism these days is `Nominal<T>` (which has no emit) or
`As<T>` (which has a minimal amount of emit):

    
    
        interface Nominal<T> {
          'nominal type tag': T
        }
    
        class As<T> {
          private tag: T
        }
    

Both are used in the same manner:

    
    
        const enum SomeTag { }
        type Something = number & As<SomeTag>
        // or
        type Something = number & Nominal<SomeTag>
    

You can even avoid the boilerplate of `const enum` if you make the `T` of `As`
or `Nominal` `T extends string`:

    
    
        type Something = number & As<'some:unique:string:tag'>
    

In both cases, the values are not unified, so:

    
    
        type Something = number & As<'kind1'>
        type SomethingElse = number & As<'kind2'>
        var x: Something = 123 as Something;
        var y: SomethingElse = 456 as SomethingElse;
        x = y;  // Type 'SomethingElse' is not assignable to type 'Something'.

~~~
TomMarius
BTW the minimal emit of the class-based example will be removed by Webpack
since the runtime representation is never used. Best option if you use a
bundler.

------
slackingoff2017
I don't see a ton of value in this. The main use case mentioned (preventing
use of functions with wrong parameter order) can be worked around using the
common 'options' object pattern that everyone uses for functions with a lot of
parameters already

Still a neat feature I guess, but IMO it's not worth the additional mental
overhead of implementing it.

A lot of problems in JS and it's typed derivatives go back to it's
'structural' type system where every object is a key-value collection and
objects with the same keys and values are interchangeble.

I really wish the ES standard would just introduce a new variable type that
has nominal typing and ditches the prototypical inheritence chain. You could
only use it with new code but since transpilation is the norm these days that
doesn't matter much

~~~
phpnode
Perhaps I should have chosen a better example, argument order is just an
illustration.

In 1998 the Mars Climate Orbiter[0] failed because of a fairly simple software
error - an imperial value was treated as metric causing the spacecraft to
calculate an invalid trajectory and burn up on entry to Mars orbit. This bug
was not detected in testing.

With opaque types this could never have happened, it would have produced a
compiler error long before the bug ever made it into production. They
eliminate an entire category of bugs.

[0]
[https://en.wikipedia.org/wiki/Mars_Climate_Orbiter](https://en.wikipedia.org/wiki/Mars_Climate_Orbiter)

~~~
thekaleb
I have come across a need for this in typescript and have used similar
approaches as the author. Maybe this is a bad outlook of mine, but I am
tending more and more to trusting my tooling over some of my fellow
developers.

------
danLugo
So basically the same as type safety through constructor constraints and
instanceof... Just a bit cleaner (maybe)

If you could extend primitives in Typescript, you'd have "amount extends
Number" and put the necessary constraints in the constructor and your operator
overloads. Maybe some or all of it could be translated to runtime checks in
javascript.

