Machen Sie so etwas wie Javas Enum mit Typescript

Ich bin einsam mit dem TypeScript-Adventskalender, daher stelle ich vor, was ich zuvor geschrieben habe.

Enum gibt es auch in Typescript, aber ich bin nicht gut darin, Funktionen zu definieren oder mehrere Werte zusammen zu behandeln. (Mit Namespace können Sie nichts anfangen.](Https://qiita.com/ConquestArrow/items/5d885128f896844698dd#%E9%96%A2%E6%95%B0%E3%82%84enum%E3 % 82% 92% E6% 8B% A1% E5% BC% B5)) Normalerweise benutze ich Java. Als ich mich mit Typescript beschäftigte, wollte ich so etwas wie Javas Enum. Also habe ich versucht, etwas wie Javas Enum in einer Klasse mit Typescript auszudrücken. (So ähnlich) Ich bin mir sicher, dass es Leute gibt, die das für selbstverständlich halten, auch wenn es ihnen nicht gesagt wird, aber ich werde es anstelle meiner eigenen Notizen belassen.

Enum Anforderungen

Lassen Sie uns zunächst auflisten, was Sie von einer Enum-ähnlichen Klasse wollen (im Folgenden als Enum-Klasse bezeichnet).

--Kann Konstanten eines festen Typs deklarieren

Code

Ich möchte eine Enum-Klasse erstellen, die die oben genannten Anforderungen mit einer Klasse erfüllt, die eine Farbe namens "Farbe" definiert. Angenommen, eine Konstante hat zwei Eigenschaften: "Name", der den Namen der Farbe darstellt, und "Hex", der die Farbe in Hexadezimalzahl darstellt.

Kann Konstanten eines festen Typs deklarieren

Um diese Anforderung zu erfüllen, definieren Sie ein Objekt der Klasse Enum als Enum-Klassenvariable. Dadurch kann der Konstantentyp die Enum-Klasse sein. Sie können Eigenschaftsdeklarationen auch erzwingen, indem Sie Objekte über den Konstruktor erstellen.

class Color {
  public static RED = new Color("red", "#ff0000")
  public static GREEN = new Color("green", "#00ff00")
  public static BLUE = new Color("blue", "#0000ff")

  public constructor(
    public name: string,
    public hex: string,
  ) { }
}

Sie können Konstanten Methoden hinzufügen

Da Konstanten Objekte der Klasse Color sind, gibt es kein Problem, wenn Sie eine Instanzmethode wie eine normale Klasse deklarieren.

class Color {
  public static RED = new Color("red", "#ff0000")
  public static GREEN = new Color("green", "#00ff00")
  public static BLUE = new Color("blue", "#0000ff")

  public constructor(
    public name: string,
    public hex: string,
  ) { }

  //Vergleich mit anderen Konstanten
  equals(other: Color) {
    //Diesmal sind die Namen gleich
    return this.name === other.name
  }

  //Konstanten als Zeichenfolgen darstellen
  toString() {
    return `${this.name}(${this.hex})`
  }

  //Definieren Sie unten die erforderlichen Methoden
}

Die Konstante ist unveränderlich

Mit dem obigen Code können Sie das deklarierte "ROT" usw. von außen ändern. Fügen Sie daher den Modifikator "readonly" hinzu, um das Objekt unveränderlich zu machen. Wenn Sie versuchen, ein neues Objekt in "Color.RED" oder "Color.GREEN" als Konstante zu deklarieren, tritt ein Kompilierungsfehler auf.

class Color {
  public static readonly RED = new Color("red", "#ff0000")
  public static readonly GREEN = new Color("green", "#00ff00")
  public static readonly BLUE = new Color("blue", "#0000ff")

  public constructor(
    public name: string,
    public hex: string,
  ) { }

  //Klassenmethode
  //Instanzmethode
}

Die Unveränderlichkeit der Konstante selbst ist garantiert, aber die Eigenschaften der Konstanten "Name" und "Hex" können geändert werden. Machen Sie daher die Immobilie auch unveränderlich. Es gibt zwei Möglichkeiten, dies zu erreichen.

Fügen Sie der Eigenschaft "readonly" hinzu

Fügen Sie der Eigenschaft genau wie beim Ändern der Konstanten "readonly" hinzu, um das Setzen zu verhindern. Diese Methode kann den Umfang der Beschreibung minimieren, aber getter wird durch den Eigenschaftsnamen festgelegt.

class Color {
  public static readonly RED = new Color("red", "#ff0000")
  public static readonly GREEN = new Color("green", "#00ff00")
  public static readonly BLUE = new Color("blue", "#0000ff")

  public constructor(
    public readonly name: string,
    public readonly hex: string,
  ) { }

  //Klassenmethode
  //Instanzmethode
}

Setzen Sie die Eigenschaft auf "privat" und implementieren Sie "getter"

Machen Sie die Immobilie von außen unsichtbar und greifen Sie über getter darauf zu. Diese Methode erhöht den Umfang der Beschreibung, verbessert jedoch die Robustheit und Wartbarkeit, da Sie "Getter" frei beschreiben können, ohne direkt auf die Eigenschaft zuzugreifen.

class Color {
  public static readonly RED = new Color("red", "#ff0000")
  public static readonly GREEN = new Color("green", "#00ff00")
  public static readonly BLUE = new Color("blue", "#0000ff")

  public constructor(
    private _name: string,
    private _hex: string,
  ) { }

  //Bei Verwendung von get accessor
  public get name() {
    return this._name 
  }

  //Wie ein normaler Getter
  public getName() {
    return this._name 
  }

  //Klassenmethode
  //Instanzmethode
}

Sie können eine der beiden auswählen, aber diesmal haben wir es mit konstanten und invarianten zu tun, so dass die erstere angemessen erscheint.

Sie können keine Konstanten von außen hinzufügen

Um diese Anforderung zu erfüllen, machen Sie den Konstruktor unsichtbar ("privat") und verbieten Sie die Erstellung von Objekten der Klasse "Farbe". Wenn Sie nun versuchen, ein neues Objekt der Klasse "Farbe" von außen zu erstellen, wird eine Fehlermeldung angezeigt.

class Color {
  public static readonly RED = new Color("red", "#ff0000")
  public static readonly GREEN = new Color("green", "#00ff00")
  public static readonly BLUE = new Color("blue", "#0000ff")

  private constructor(
    public readonly name: string,
    public readonly hex: string,
  ) { }

  //Klassenmethode
  //Instanzmethode
}

Sie können eine Liste deklarierter Konstanten in einem unveränderlichen Zustand erhalten

Lassen Sie uns die Realisierung auf zwei Arten betrachten.

Zur Liste vom Konstruktor hinzufügen

Machen Sie die Liste wie bei konstanten Eigenschaften zu einer "privaten" Klassenvariablen, damit sie nicht von außen bearbeitet wird, und bereiten Sie Ihren eigenen "Getter" für den Zugriff von außen vor. Um der Liste etwas hinzuzufügen, können Sie das durch this deklarierte Objekt im Konstruktor abrufen, also werden wir dies hinzufügen.

Selbst wenn die Anzahl der Konstanten zunimmt, ist es bei dieser Methode nicht erforderlich, den Teil zum Hinzufügen der Liste zu beschreiben, damit Sie nicht vergessen, die Konstanten hinzuzufügen. Sie müssen jedoch Ihren eigenen "Getter" vorbereiten. (Wenn Sie den get-Accessor für "getter" verwenden, besteht auch das Problem der Überlappung mit dem Eigenschaftsnamen.)

class Color {
  private static _values = new Array<Color>()

  public static readonly RED = new Color("red", "#ff0000")
  public static readonly GREEN = new Color("green", "#00ff00")
  public static readonly BLUE = new Color("blue", "#0000ff")

  private constructor(
    public readonly name: string,
    public readonly hex: string,
  ) { 
    Color._values.push(this)
  }

  public get values() {
    return this._values
  }

  //Klassenmethode
  //Instanzmethode
}

Hinzufügen, um sich selbst aufzulisten

Ich weiß nicht, ob es eine Nutzungsszene gibt, aber es ist ein Muster, die Liste selbst zu deklarieren. Verwenden Sie immutable.js, um die Liste unveränderlich zu machen. immutable.js ist eine nette Bibliothek zum einfachen Deklarieren unveränderlicher Listen und Karten. Eine ausführliche Erklärung finden Sie hier.

Auf diese Weise müssen Sie den Konstruktor nicht verschmutzen, um eine Liste hinzuzufügen, und Sie müssen nicht einmal einen "Getter" für den externen Zugriff einfügen, wodurch Sie den gesamten Code besser sehen können. Wenn jedoch die Anzahl der Konstanten zunimmt, tritt das Problem auf, dass sich der in der Liste beschriebene Teil ausbaucht, und es ist leicht zu vergessen, die Konstanten hinzuzufügen.

import { List } from 'immutable'

class Color {

  public static readonly RED = new Color("red", "#ff0000")
  public static readonly GREEN = new Color("green", "#00ff00")
  public static readonly BLUE = new Color("blue", "#0000ff")

  public static readonly values = List.of(
    Color.RED, Color.GREEN, Color.BLUE
  )

  private constructor(
    public readonly name: string,
    public readonly hex: string,
  ) { }

  //Klassenmethode
  //Instanzmethode
}

Ich mag, welche ich verwenden soll, aber dieses Mal werde ich die verwenden, um sie im Konstruktor hinzuzufügen.

Sie können eine Konstante aus einer bestimmten Variablen erhalten

Da dies eine Voraussetzung für eine Klasse ist, definieren wir eine Methode, die eine Konstante als Klassenmethode zurückgibt.

class Color {
  private static _values = new Array<Color>()

  public static readonly RED = new Color("red", "#ff0000")
  public static readonly GREEN = new Color("green", "#00ff00")
  public static readonly BLUE = new Color("blue", "#0000ff")

  private constructor(
    public readonly name: string,
    public readonly hex: string,
  ) { 
    Color._values.push(this)
  }

  public get values() {
    return this._values
  }

  //Holen Sie sich Konstante vom Namen
  static fromName(name: string) {
    return this.values.find(color => color.name === name)
  }
  
  //Klassenmethode
  //Instanzmethode

}

Zusammenfassung

Der bisherige Code kann wie folgt zusammengefasst werden.

class Color {
  private static _values = new Array<Color>()

  public static readonly RED = new Color("red", "#ff0000")
  public static readonly GREEN = new Color("green", "#00ff00")
  public static readonly BLUE = new Color("blue", "#0000ff")

  private constructor(
    public readonly name: string,
    public readonly hex: string,
  ) { 
    Color._values.push(this)
  }

  static get values() {
    return this._values
  }
  
  static fromName(name: string) {
    return this.values.find(color => color.name === name)
  }
  
  equals(other: Color) {
    return this.name === other.name
  }

  toString() {
    return `${this.name}(${this.hex})`
  }

}

Es gibt auch die Frage, wie viel Aufwand für den Umgang mit Konstanten erforderlich ist. Angesichts der Wartbarkeit und Erweiterbarkeit halte ich es jedoch für sehr nützlich, dass Typinferenz funktioniert und Methoden geschrieben werden können. Als nächstes überlegen wir, ob wir mit Typinferenz intelligenter schreiben können.

Wenn Sie Verbesserungen am Code haben, können Sie dies gerne kommentieren.

Bonus

Erzielen Sie Java valueOf

Die Erklärung von valueOf lautet hier. Kurz gesagt bedeutet dies "Suche nach dem deklarierten Konstantennamen und Rückgabe der passenden Konstante".

Versuchen wir, dies mit Gewalt unter der Prämisse zu erreichen, dass "Konstanten alle in Großbuchstaben geschrieben sind". Wenn Sie nicht filtern müssen, können Sie es einfach mit "Farbe [Name]" abrufen.

class Color {
  //Kürzung
  
  static valueOf(name: keyof typeof Color) {
    if(/^[A-x]+$/.test(name)) {
      return Color[name] as Color
    }
    return undefined
  }

}

Hinweis

Wenn Sie es mit "Farbe [Name]" erhalten, wird ein Kompilierungsfehler angezeigt, wenn "Name" nicht mit dem "Schlüssel des Farbtyps" übereinstimmt. (Die Oberseite ist ein Fehler, die Unterseite ist in Ordnung)

image.png

Recommended Posts

Machen Sie so etwas wie Javas Enum mit Typescript
[Java] Verzweigungsaufzählung mit switch-Anweisung
[Java] Reduzieren Sie if-Anweisungen mit Enum
Erstellen Sie mit JavaScript eine leistungsstarke Aufzählung mit Feldern und Methoden wie Java
[Java] enum (Aufzählungstyp)
Schnittstelle Versuchen Sie, Java-Problem TypeScript 7-3 zu machen
Implementieren Sie so etwas wie einen Stack in Java
[Java] Machen Sie das Programm mit parallelStream 10x schneller
[Erstes Java] Machen Sie etwas, das vorerst mit Intellij funktioniert
Ich habe versucht, eine Standardauthentifizierung mit Java durchzuführen
Machen Sie die SpringBoot1.5 + Gradle4.4 + Java8 + Docker-Umgebung mit Java11 kompatibel
[Java] Erstellen Sie so etwas wie eine Produktsuch-API
Inkrementierungsverhalten Versuchen Sie, Java zum Problem TypeScript 3-4 zu machen
Zeichenfolgenoperation Versuchen Sie, das Java-Problem in TypeScript 9-3 zu ändern
Etwas über Java
Komfortablere Java-Codeabdeckung mit Jacoco 0.8.0
Machen Sie Java Stream Zeilenumbrüche mit Eclipse schön
Einfacher LINE BOT mit Java Servlet
Ich möchte so etwas wie "cls" in Java machen
Initialisierung von for Versuchen Sie, das Java-Problem in TypeScript 5-4 zu ändern
Machen Sie mit JavaFX erstellte Kalender-Gadgets mit Java SE 9 kompatibel
[Java-Grundlagen] Lassen Sie uns ein Dreieck mit einer for-Anweisung erstellen
Implementieren Sie Singleton mit Enum schnell in Java
Machen Sie mit Spring's Abstract Routing DataSource etwas Sharding!
Ich habe nc (netcat) normalerweise mit JAVA gemacht
Installieren Sie Java mit Homebrew
Machen Sie einen Blackjack mit Java
Wechseln Sie die Plätze mit Java
Installieren Sie Java mit Ansible
Bequemer Download mit JAVA
Schalten Sie Java mit direnv
Enum Reverse Map Java
Java-Download mit Ansible
Lass uns mit Java kratzen! !!
Erstellen Sie Java mit Wercker
Endian-Konvertierung mit JAVA
[Anfänger] Versuchen Sie, mit Java ein einfaches RPG-Spiel zu erstellen ①
Ich möchte eine Liste mit Kotlin und Java erstellen!
Ich möchte eine Funktion mit Kotlin und Java erstellen!
Java lernen: Verwenden Sie Timer, um so etwas wie einen Bomben-Timer zu erstellen