0

I have the following typescript type:

export class Status {
  static readonly DONE = new Status('DONE', 'p-tag-green');
  static readonly PENDING = new Status('PENDING', 'p-tag-yellow');

  private constructor(
    private _value: string,
    private _class: string
  ) { }
}

and it is used in

export interface Reminder {
  id: string;
  status: Status;
}

When I make my httpClient request the JSON {"id": 5, "status": "DONE"} is returned, so the status field of the parsed Reminder object has the string value of DONE but it is not actually an object of type Status, thus I cannot retrieve the value property of the Status object.

I can get a Status object, if I manually map the response like so:

return this.httpClient.get(`api/v1/reminders/${id}`).pipe(
  map((data: any) => ({
    ...data,
    status: Reminder.Status[data['status'] as keyof typeof Reminder.Status]
  }))
);

However, since I have a lot of cases like this one, is it somehow possible, to make this more generic, so that any interface like Reminder which includes any such pseudo-enum type like Status? I was thinking about http client interceptors or decorators, but couldn't make any of them work.

  • Decorators because I'd need to make the interface a class which implies more changes I don't like
  • Http client interceptor because I don't have the types of the interface/classes that I need to parse to. However, maybe there is some clever prototype inspection that can be done?
2
  • is your real intention to turn a string ("DONE" | "PENDING") into a class and add extra information like 'p-tag-green' or 'p-tag-yellow' as you shown in the example? Commented May 27, 2023 at 19:01
  • Yes. My business domain has an enumeration - DONE|PENDING, and I'd like to associate these values with extra information in a compile-time check. Thus, anytime a new status is added, the compiler forces me to choose tag color. Commented May 28, 2023 at 5:36

1 Answer 1

0

In my opinion this code is over-engineered you don't need to create a static Class to istanciate one single time the status Object, this approach can lead to problems when you try to update Status from another place becouse the reference is only one. You can follow the immutable way, in your case, create a new Status each time you recive a response.

export type StatusType = 'DONE' | 'PENDING';

export interface IReminder {
  id: string;
  status: StatusType;
}

// think about this object like a configuration file, something that can be centralized
export const STATUS_STYLES = { 
   DONE: 'p-tag-green', // or a structurated object
   PENDING: 'p-tag-yellow'
}

export class Status {
   value: string;
   style: string;

   constructor(status: StatusType){
       this.value = status;
       this.style = STATUS_STYLES[status];
   }
}

export class Reminder {
  id: string;
  status: Status;

  constructor(reminder: IReminder) {
     this.id = reminder.id;
     this.status = new Status(status)
  }
}

return this.httpClient.get(`api/v1/reminders/${id}`).pipe(
  map((reminder: Reminder) => new Reminder(reminder)))
);

you can organize this code into different files, i usually use a file for interfaces and classes called .model.ts a file for types and a file or a json for configurations. This approach keep code simple to read and to extend, removing from map operator business logics.

Sign up to request clarification or add additional context in comments.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.