You're asking for opaque types. This is kinda possible in typescript but you can only approximate it unfortunately.
You may also want to consider a branded type, which can be useful but a little less strict.
You may also want to consider a branded type, which can be useful but a little less strict.
declare class _MySpecialPrimitive {
private mySpecialPrimitive?: never;
}
type MySpecialPrimitive = number & _ MySpecialPrimitive;
Combined with type guards (which you can conditionally execute at runtime), you can narrow overly broad structural types safely, eg: const isMySpecialPrimitive(val: unknown): val is MySpecialPrimitive => /* anything that produces a boolean */
(And you can use assert guards in a similar way, but I find they make it easier to treat them as a noop) const MySpecialPrimitiveBrand = Symbol('mySpecialPrimitive')
const val = Object.assign(prevVal, { [MySpecialPrimitiveBrand]: doesntMatter })
Yeah, don’t do that unless you want to destroy every language facility that depends on reference equality checks. It boxes your primitive and breaks the world.The class/private approach has the advantage of hiding the extra property from everything except the type checker.
For complex types it also disallows spreading to prevent circumventing validation. However, errors are often not has human readable as one would like them to be.