Fügen Sie einer Aufzählung Funktionen hinzu

78

Ist es möglich, einem Enum-Typ in TypeScript Funktionen hinzuzufügen?

zum Beispiel:

enum Mode {
    landscape,
    portrait,

    // the dream...
    toString() { console.log(this); } 
}

Oder:

class ModeExtension {
    public toString = () => console.log(this);
}

enum Mode extends ModeExtension {
    landscape,
    portrait,
}

Natürlich toString()würde die Funktion so etwas wie ein enthalten. switchAber ein Anwendungsfall würde folgendermaßen verlaufen:

class Device {
    constructor(public mode:Mode) {
        console.log(this.mode.toString());
    }
}

Ich verstehe, warum enumes eine seltsame Sache sein kann, ein zu erweitern, und frage mich nur, ob es möglich ist.

Herr Baudin
quelle

Antworten:

101

Sie können entweder eine Klasse haben, die von der Aufzählung getrennt ist, und sie verwenden, um die gewünschten Dinge abzurufen, oder Sie können einen Namespace in die Aufzählung einbinden und alles an einer Stelle abrufen, die an derselben Stelle aussieht.

Modus-Dienstprogrammklasse

Dies ist also nicht genau das, wonach Sie suchen, aber dies ermöglicht es Ihnen, das Verhalten von "Mode to String" mithilfe einer statischen Methode zu kapseln.

class ModeUtil {
    public static toString(mode: Mode) {
        return Mode[mode];
    }
}

Sie können es so verwenden:

const mode = Mode.portrait;
const x = ModeUtil.toString(mode);
console.log(x);

Modus Enum / Namespace Merge

Sie können einen Namespace mit der Aufzählung zusammenführen, um mit zusätzlichen Methoden eine Aufzählung zu erstellen:

enum Mode {
    X,
    Y
}

namespace Mode {
    export function toString(mode: Mode): string {
        return Mode[mode];
    }

    export function parse(mode: string): Mode {
        return Mode[mode];
    }
}

const mode = Mode.X;

const str = Mode.toString(mode);
alert(str);

const m = Mode.parse(str);
alert(m);
Fenton
quelle
4
Gibt es eine Möglichkeit, dem Enum-Mitglied eine Methode hinzuzufügen?
Jay Wick
Gibt es Vorteile bei der Verwendung des Moduls gegenüber dem Unterricht?
Daniel
2
@ Daniel - Ich habe die Antwort so aktualisiert, wie sie modulejetzt ist namespace. Ich habe auch den Hauptvorteil eines Namespace in diesem speziellen Fall hervorgehoben, nämlich die Methoden, die unter der Aufzählung erscheinen, d Mode. H.
Fenton
@Fenton - Ich möchte import { Mode } from "./Mode.ts"und verwende es auf die gleiche Weise wie in Ihrem Verwendungsbeispiel - ist dies möglich, wenn Namespaces zusammengeführt werden?
Vedran
1
Es ist erwähnenswert, dass der namespaceAnsatz möglicherweise Probleme in Funktionen verursacht, die die "Aufzählung" verbrauchen (z. B. um Aufzählungsmitglieder zu iterieren, um eine Auswahlliste zu füllen) und unerwartete Ergebnisse wie "myAddedMethod () {...}" erzeugen. Sie können fremde Nicht-Enum-Mitglieder am Iterationspunkt herausfiltern, aber nur darauf hinweisen, dass dieser Ansatz nicht ohne Nachteile ist.
virtualeyes
30

Sie können den Zeichenfolgenwert einer nicht konstanten Aufzählung mithilfe eckiger Klammern abrufen:

class Device {
    constructor(public mode:Mode) {
        console.log(Mode[this.mode]);
    }
}

Sie können auch einige enumspezifische Util-Funktionen in die Enum einfügen, aber das ist genau wie bei statischen Klassenmitgliedern:

enum Mode {
    landscape,
    portrait
}

namespace Mode {
    export function doSomething(mode:Mode) {
        // your code here
    }
}
Benjamin
quelle
1
import Mode = require('Mode'); ... /*more code here...*/ <Mode.landscape> landScape = Mode.landscape;und ich habe Fehler bekommen: error TS2503: Cannot find namespace 'Mode'
Pawciobiel
1
@pawciobiel Nun, ich denke, es Modeist der Typ, den Sie hier besetzen möchten und nicht landscape. Ändern Sie Ihre Besetzung von <Mode.landscape>nur<Mode>
Benjamin
Nur ein Hinweis, dass dies in TS 1.8.10 nicht funktioniert. Ich musste ein exportvor der Moduldefinition hinzufügen .
icfantv
@icfantv Es funktioniert unverändert auf typescriptlang.org/play (Hinzufügen der Zeile Mode.doSomething(Mode.landscape);am Ende zum Testen)
Benjamin
1
Ich denke, das lag daran, dass meine Aufzählung dies auch hatte exportund das Problem ist, dass TS beide Objekte entweder exportieren muss oder nicht. Entschuldigung für die Verwirrung.
icfantv
12

Konvertieren Sie Ihre enumin das Aufzählungsmuster. Ich finde, dass dies im Allgemeinen für viele Sprachen eine bessere Vorgehensweise ist, da Sie ansonsten die Kapselungsoptionen für Ihren Typ einschränken. Die Tendenz ist, switchauf den Aufzählungswert zu setzen, wenn wirklich alle Daten oder Funktionen, die von dem bestimmten Aufzählungswert abhängen, nur in jede Instanz der Aufzählung eingehen sollten. Ich habe etwas mehr Code hinzugefügt, um zu demonstrieren.

Dies funktioniert möglicherweise nicht, wenn Sie besonders von den zugrunde liegenden Aufzählungswerten abhängig sind. In diesem Fall müssten Sie ein Mitglied für die alten Werte hinzufügen und Orte konvertieren, die es benötigen, um die neue Eigenschaft zu verwenden.

class Mode {
   public static landscape = new Mode(1920, 1080);
   public static portrait = new Mode(1080, 1920);

   public get Width(): number { return this.mWidth; }
   public get Height(): number { return this.mHeight; }

   // private constructor if possible in a future version of TS
   constructor(
      private mWidth: number,
      private mHeight: number
   ) {
   }

   public GetAspectRatio() {
      return this.mWidth / this.mHeight;
   }
}
Dave Cousineau
quelle
1
Ihr eigenes Beispiel kann als Gegenargument zu Ihrem Standpunkt angesehen werden. Höhe / Breite variieren oftmals aufgrund anderer Dinge, nicht nur "Landschaft gegen Porträt" - sie gehören nicht zu dieser Aufzählung. Diese Aufzählung drückt wirklich nur eine vage Präferenz aus, ihre genaue Bedeutung hängt vom Kontext ab.
Vsevolod Golovanov
1
@VsevolodGolovanov sicher, es ist nur ein Beispiel, das zeigt, wie man es einrichtet. Ich schlage nicht vor, eine ModeAufzählung mit diesen Werten zu verwenden. Verwenden Sie alle Werte, die für Ihr Projekt relevant sind.
Dave Cousineau
Was @VsevolodGolovanov sagt, denke ich, ist, dass ein Wertobjekt (wie Sie vorgeschlagen haben) keine Aufzählung ist und dass sie unabhängig davon, welche Werte Sie verwenden, nicht wirklich zur Aufzählung gehören.
Christian
1
@Christian sagte er, dass "Breite", "Höhe" und "Seitenverhältnis" nicht wirklich Eigenschaften von "Landschaft" und "Hochformat" sind; dh: das Konzept der Landschaft ist unabhängig von der Größe. Das ist wahr, aber es ist nur ein Beispiel.
Dave Cousineau
2

kann enum wie durch privaten Konstruktor und statisches get return Objekt machen

export class HomeSlideEnum{

  public static get friendList(): HomeSlideEnum {

    return new HomeSlideEnum(0, "friendList");

  }
  public static getByOrdinal(ordinal){
    switch(ordinal){
      case 0:

        return HomeSlideEnum.friendList;
    }
  }

  public ordinal:number;
  public key:string;
  private constructor(ordinal, key){

    this.ordinal = ordinal;
    this.key = key;
  }


  public getTitle(){
    switch(this.ordinal){
      case 0:

        return "Friend List"
      default :
        return "DChat"
    }
  }

}

dann kann später so verwendet werden

HomeSlideEnum.friendList.getTitle();
David Valentino
quelle
0

Eine Ergänzung zu Fentons Lösung. Wenn Sie diesen Enumerator in einer anderen Klasse verwenden möchten, müssen Sie sowohl die Aufzählung als auch den Namespace exportieren. Es würde so aussehen:

export enum Mode {
    landscape,
    portrait
}

export namespace Mode {
    export function toString(mode: Mode): string {
        return Mode[mode];
    }
}

Dann importieren Sie einfach die Datei mode.enum.ts in Ihre Klasse und verwenden sie.

Stefanos Kargas
quelle