what I am looking at while reviewing and debugging is Typescript not Javascript.
Perhaps this is the core issue.
TypeScript is nothing but a JavaScript dialect.
If JavaScript cannot easily use code output from TypeScript then TypeScript has failed.
I view TypeScript as a JavaScript with some additional information so that the tooling can perform static type checking - or rather static type linting.
as const a const assertion is the first bit of "type space" information. It conveys that modifying userStatus would be an error; as a consequence of being read-only we can now base new literal types on this read-only value.
typeof userStatus creates a new type based on the whole userStatus value (typeof type operator).
all those values in all the files that use them have to be found and changed (difficult or impossible if you don't control the code that uses the exported values).
With the object using userStatus.NOT_FOUND decouples the code from the raw string value. Just change the userStatus object values
The constants-in-an-object approach interoperates with JavaScript with a minimum amount of friction. So while it may be "ugly" it's functional.
TypeScript will eventually deprecate features that don't align with JavaScript. For example namespace (formerly "internal module") seems to be on its way out as there is no JavaScript equivalent (namespacing is often emulated with objects), and ECMAScript private fields could be taking over for private members.
Perhaps this is the core issue.
TypeScript is nothing but a JavaScript dialect.
If JavaScript cannot easily use code output from TypeScript then TypeScript has failed.
I view TypeScript as a JavaScript with some additional information so that the tooling can perform static type checking - or rather static type linting.
There is the notion of type declaration space and variable declaration space:
The unfortunate thing is that TypeScript source code conflates both type and value space.
This is how one might gather together related constants in JavaScript (value space) - in effect emulating an enum.
as const
a const assertion is the first bit of "type space" information. It conveys that modifyinguserStatus
would be an error; as a consequence of being read-only we can now base new literal types on this read-only value.typeof userStatus
creates a new type based on the wholeuserStatus
value (typeof type operator).keyof typof userStatus
extracts the object property keys as a union (keyof type operator).typeof userStatus[keyof typeof userStatus]
finally extracts the object values as a union (mapped types).
So the object is the JavaScript (value space) part.
The rest (type space) communicates to TypeScript what that object represents at compile time.
The issue with the plain union
is that the actual string values are used in the code. If for business reasons you need to change to
all those values in all the files that use them have to be found and changed (difficult or impossible if you don't control the code that uses the exported values).
With the object using
userStatus.NOT_FOUND
decouples the code from the raw string value. Just change theuserStatus
object valuesThe issue with TypeScript enums is that they aren't a great fit for JavaScript and were largely inspired by C# enumerations. The ECMAScript Enums proposal doesn't seem to be going anywhere.
The constants-in-an-object approach interoperates with JavaScript with a minimum amount of friction. So while it may be "ugly" it's functional.
TypeScript will eventually deprecate features that don't align with JavaScript. For example
namespace
(formerly "internal module") seems to be on its way out as there is no JavaScript equivalent (namespacing is often emulated with objects), and ECMAScript private fields could be taking over for private members.now,
const Obj as const
make su much more sense. and it's amazingly useful!thank you so much for sharing such an interesting insight!