Home > Software engineering >  TypeScript define predefined values for model class
TypeScript define predefined values for model class

Time:03-05

Let's say i have a User class and i need to instance only predefined 'status'. Is this the best way to do it? Is there any other alternative? What`s the right way to do it? Thanks in advance.

export class User {
    constructor(
        username: string,
        email: string,
        status: AccountStatus
    ){}
}

export enum AcountStatus {
    Active = 'Active',
    Suspended = 'Suspended'
}

import { User } from 'app/models/user.model'
import { AccountStatus } from 'app/models/user.model'

private user: User;

this.user = new User('username', '[email protected]', AccountStatus.Active) 

CodePudding user response:

If most of the users will have a default Active status, then you can give a default value to status in the constructor as well:

class User {
  constructor(
    public username: string,
    public email: string,
    public status: AccountStatus = AccountStatus.Active
  ){}
}

Then when you initialize an Active user, you don't need to explicitly pass in the Active status every time:

let user: User;

user = new User('username', '[email protected]')

playground.


Another possible improvement is you can replace the AccountStatus enum with a union type:

class User {
    constructor(
      public username: string,
      public email: string,
      public status: 'Active' | 'Suspended' = 'Active'
    ){}
}

let user = new User('username', '[email protected]') 

CodePudding user response:

If you want to give the consumer of your code more flexibility, you can accept either a union of string literals OR a member from your string enum:

TS Playground

type Values<T> = T[keyof T];
type StringEnumToUnion<T extends Record<string, string>> = `${Values<T>}`;

function valueIsStringEnum <Enum extends Record<string, string>>(
  value: unknown,
  o: Enum,
): value is Values<Enum> {
  return Object.values(o).includes(value as string);
}

function assert (expr: unknown, msg?: string): asserts expr {
  if (!expr) throw new Error(msg);
}

export enum AccountStatus {
  Active = 'Active',
  Suspended = 'Suspended',
}

export class User {
  constructor (
    username: string,
    email: string,
    status: StringEnumToUnion<typeof AccountStatus>,
  ) {
    let ac: AccountStatus;

    ac = status; /*
    ~~
    Type '"Active" | "Suspended"' is not assignable to type 'AccountStatus'.
      Type '"Active"' is not assignable to type 'AccountStatus'.(2322) */

    // Assert it back to AccountStatus for your own usage if you want:
    assert(valueIsStringEnum(status, AccountStatus));
    ac = status; // ok
  }
}


// Example usage:

new User('user', '[email protected]', 'ACTUVE'); /*
                                  ~~~~~~~~
Argument of type '"ACTUVE"' is not assignable to parameter of type '"Active" | "Suspended"'.(2345) */

new User('user', '[email protected]', 'Active'); // ok
new User('user', '[email protected]', AccountStatus.Active); // ok

  • Related