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'.
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.
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
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.
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.
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.