Opaque Types in Flow and TypeScript
codemix.com
codemix.com
const enum _A = {};
type A = number & _A;
This comes in handy for IDs and other primitives shared by multiple complex types.https://basarat.gitbooks.io/typescript/docs/tips/nominalTypi...
(edit:) Ah ha! "Because numbers are type compatible with enums the previous technique cannot be used for them."
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'.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
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.
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.