Stéphane
Stéphane•7mo ago

How to use a class name as a qualifier for an enum ?

Hi there, it's more of a generic typescript question but I'm using it in the Deno context and the linter throws some warnings at my code. I'm trying to do this little helper class for rich text logging in the terminal :
export class RichText {
// deno-lint-ignore no-explicit-any
private _data: any;
private _properties: Array<
| RichText.COLOR
| RichText.BACKGROUND
| RichText.STYLE
>;

static RESET = "\x1b[0m";

constructor(
// deno-lint-ignore no-explicit-any
data: any,
...properties: Array<
| RichText.COLOR
| RichText.BACKGROUND
| RichText.STYLE
>
) {
this._data = data;
this._properties = properties;
}

toString() {
return this._properties.join("") + `${this._data}${RichText.RESET}`;
}
}
// deno-lint-ignore no-namespace
export namespace RichText {
export enum COLOR {
RED = "\x1b[31m",
GREEN = "\x1b[32m",
YELLOW = "\x1b[33m",
BLUE = "\x1b[34m",
MAGENTA = "\x1b[35m",
CYAN = "\x1b[36m",
WHITE = "\x1b[37m",
}

export enum BACKGROUND {
RED = "\x1b[41m",
GREEN = "\x1b[42m",
YELLOW = "\x1b[43m",
BLUE = "\x1b[44m",
MAGENTA = "\x1b[45m",
CYAN = "\x1b[46m",
WHITE = "\x1b[47m",
}

export enum STYLE {
BRIGHT = "\x1b[1m",
DIM = "\x1b[2m",
UNDERSCORE = "\x1b[4m",
BLINK = "\x1b[5m",
REVERSE = "\x1b[7m",
HIDDEN = "\x1b[8m",
}
}
export class RichText {
// deno-lint-ignore no-explicit-any
private _data: any;
private _properties: Array<
| RichText.COLOR
| RichText.BACKGROUND
| RichText.STYLE
>;

static RESET = "\x1b[0m";

constructor(
// deno-lint-ignore no-explicit-any
data: any,
...properties: Array<
| RichText.COLOR
| RichText.BACKGROUND
| RichText.STYLE
>
) {
this._data = data;
this._properties = properties;
}

toString() {
return this._properties.join("") + `${this._data}${RichText.RESET}`;
}
}
// deno-lint-ignore no-namespace
export namespace RichText {
export enum COLOR {
RED = "\x1b[31m",
GREEN = "\x1b[32m",
YELLOW = "\x1b[33m",
BLUE = "\x1b[34m",
MAGENTA = "\x1b[35m",
CYAN = "\x1b[36m",
WHITE = "\x1b[37m",
}

export enum BACKGROUND {
RED = "\x1b[41m",
GREEN = "\x1b[42m",
YELLOW = "\x1b[43m",
BLUE = "\x1b[44m",
MAGENTA = "\x1b[45m",
CYAN = "\x1b[46m",
WHITE = "\x1b[47m",
}

export enum STYLE {
BRIGHT = "\x1b[1m",
DIM = "\x1b[2m",
UNDERSCORE = "\x1b[4m",
BLINK = "\x1b[5m",
REVERSE = "\x1b[7m",
HIDDEN = "\x1b[8m",
}
}
and I want to be able to just import RichText and use it like that : console.log(new RichText("coucou", RichText.COLOR.BLUE, RichText.STYLE.BRIGHT)); which actually works, but I wonder if there's an "official" clean way to do it. I don't want to import generic terms such as COLOR or STYLE so I'd like them to be qualified with the same name as the class for clarity's sake.
2 Replies
marvinh.
marvinh.•7mo ago
Your solution is perfect if you want to store the enum on the class as a property. There is no other way to do that in TS other than the namespace workaround that you're already using. Using namespace is getting less common by the day since JS has a proper module system now, unless you want to mirror patterns from other programming languages like in your example. The reason why most devs avoid the namespace part of TS, is because they evaluate to an IIFE which makes them impossible to be tree-shaken out in case you need to ship your code to network constrained JS environments like browsers. So instead of creating internal modules via namespaces, folks tend to simply use the module boundary for that.
export const RESET = "\x1b[0m";
export enum COLOR {
// ...
}
export enum BACKGROUND {
// ...
}
export enum STYLE {
// ...
}

export class RichText {
// ...
}
export const RESET = "\x1b[0m";
export enum COLOR {
// ...
}
export enum BACKGROUND {
// ...
}
export enum STYLE {
// ...
}

export class RichText {
// ...
}
And now we can use the module boundary as a namespace:
// We picked Terminal here, but the user
// can pick any identifier name they want
import * as Terminal from "./RichText.ts";

console.log(new Terminal.RichText("coucou", Terminal.COLOR.BLUE, Terminal.STYLE.BRIGHT));
// We picked Terminal here, but the user
// can pick any identifier name they want
import * as Terminal from "./RichText.ts";

console.log(new Terminal.RichText("coucou", Terminal.COLOR.BLUE, Terminal.STYLE.BRIGHT));
Looking at the class content itself it seems like you're interested in logging colored terminal output. There is a module for that in Deno's standard library that most folks use: https://deno.land/std@0.208.0/fmt/colors.ts
Stéphane
Stéphane•7mo ago
Thanks a lot for your answer. That's the conclusion I got by looking through diverse stackoverflow threads, but it's good to see I didn't overlook something. And yeah I tried to get something closer to C++ where an enum can be nested in a class so I could have just one export embedding all the class features. The import * as trick is ok I guess. And yeah I didn't even think about looking at the std lib haha Thanks again, moving the thread to solved 🙂