Búsqueda de sitios web

¿Cómo creo enums anidados en Typescript?


Programador de Java aquí. Estoy configurando algún tipo de construcción de enum anidado. A continuación hay un breve ejemplo anotado de lo que estoy trabajando. Sé cómo resolver esto en Java, pero no puedo averiguar cómo hacerlo en Typescript.

Básicamente tengo una lista de NombresDeEntidad, cada NombreDeEntidad tiene un montón de NombresDeTipoDeEntidad. Quiero crear funciones/métodos que acepten el NombreDeTipoDeEntidad como argumento y puedan realizar alguna lógica que también pueda encontrar el NombreDeEntidad al que pertenece este NombreDeTipoDeEntidad. Es importante poder pasar argumentos a estas funciones/métodos fácilmente sin redundancia. Actualmente estoy pasando el NombreDeEntidad y el NombreDeTipoDeEntidad, lo cual es redundante.

(Estoy usando TypeScript 4.5.4)

export class Afs {
  public static oneMethod(entityName: Afs.EntityName) {
    // This method just uses the entityName.
  }

  public static someMethod(entityTypeName: Afs.EntityTypeName) {
    // This method uses the entityTypeName and
    // the entityName. However, passing both
    // feels redundant. How to get the entityNames 
    // here, without passing the entityName argument?
  }

}

export namespace Afs {

  export enum EntityName {
    // Simplified. Much more entitynames.
    address = 'address',
    condition = 'condition',
  }

  /* In Java I would create an EntityTypeName 
  ** interface that is implemented by the enums 
  ** with a static method getEntityName() */
  export type EntityTypeName = EntityTypeName.Address 
  | EntityTypeName.Condition


  // I would implement the interface here.
  export namespace EntityTypeName {
    export enum Address {
      default = 'default',
      // enum method from interface 
      // getEntityName() { return Afs.EntityName.Address }
    }
  
    export enum Condition {
      // Simplified. Much more 
      conditionDetails = "conditionDetails",
      employmentConditions = "employmentConditions",
    }  
  }

}

Actualizar

Probé la solución de @mike-clark y ¡funciona! (¡Gracias!!!) Sin embargo, parece mucha duplicación de código, tenía algo de esperanza de que habría una solución más simple. El mundo real tiene 29 entityNames y más de 350 entityTypeNames.

export class Afs {
  public static oneMethod(entityName: Afs.EntityName) {
    console.log(entityName.get());
  }
  public static someMethod(entityTypeName: Afs.EntityTypeName.Generic) {
    console.log(entityTypeName.getEntityName().get());
    console.log(entityTypeName.get());
  }
}

export namespace Afs {

  export class EntityName {
    public static address = new EntityName('address');
    public static condition = new EntityName('condition');
    private constructor(private entityName: string) { }
    public get(): string {
      return this.entityName
    }
  }

  export namespace EntityTypeName {

    export interface Generic {
      get(): string;
      getEntityName(): EntityName;
    }

    export class Address implements Generic {
      public static default = new Address(EntityName.address, 'default');
      private constructor(private entityName: EntityName, private entityTypeName: string) {}
      get(): string { return this.entityTypeName }
      getEntityName(): EntityName { return this.entityName }
    }

    export class Condition implements Generic {
      public static conditionDetails = new Condition(EntityName.condition, 'conditionDetails');
      public static employeeConditions = new Condition(EntityName.condition, 'employeeConditions')
      private constructor(private entityName: EntityName, private entityTypeName: string) {}
      get(): string { return this.entityTypeName }
      getEntityName(): EntityName { return this.entityName }
    }

  }

}

Respuesta #1

No puedes agregar métodos que se puedan llamar en los miembros de enum en TypeScript. Aunque es decepcionante (en un sentido orientado a objetos), la solución práctica que se suele usar son las funciones utilitarias estáticas externas que hacen un switch en el enum y manejan todos los casos.

Puedes adjuntar métodos estáticos al enum, lo cual es una manera conveniente de agrupar y hacer un seguimiento de las funciones de utilidad que te ayudan a trabajar con los "metadatos" del enum.

Puedes implementar un patrón al estilo enum que permita métodos que se puedan llamar en los miembros del enum. Pero estos no son enums de TypeScript de primera clase, aunque en la mayoría de los casos son un reemplazo suficiente. Se siente decepcionante abandonar el enum incorporado por algo que creas tú mismo, pero este es un patrón que la gente sí utiliza.

La razón técnica actual es que el valor asignado a cada clave del enum es un literal de cadena, que no tiene un prototipo común compartido solo por los valores clave del enum. Dado este TypeScript:

enum MyEnum {
    A='A',
    B='B'
}

El código JavaScript generado es:

var MyEnum;
(function (MyEnum) {
    MyEnum["A"] = "A";
    MyEnum["B"] = "B";
})(MyEnum || (MyEnum = {}));

Como puedes ver, no hay un prototipo o clase común para los valores de los miembros, por lo que no podemos "hackear" nuestra forma de agregar funciones a los valores de los miembros a menos que extendamos la clase global String, lo cual es una Mala Idea. TypeScript necesitaría complicar el diseño de los enums envolviendo los valores de los miembros en una segunda clase con un prototipo común (o al menos una interfaz común, expresada o implícita), y supongo que los diseñadores del lenguaje no querían introducir ese nivel de complejidad.

Artículos relacionados