Typescript: Kann eingebaute Typen nicht erweitern

Erstellt am 14. Nov. 2014  ·  14Kommentare  ·  Quelle: microsoft/TypeScript

Der ES6 bezeichnet einige native Objekte als unterklassifizierbar, z. B. Objekt, Boolescher Wert, Fehler, Karte, Versprechen usw. Die Art und Weise, wie diese Konstrukte in lib.d.ts modelliert werden, kann nicht in Unterklassen unterteilt werden, da sie als Paar aus einer var und einer Schnittstelle definiert sind.

Aus der ES6-Spezifikation Abschnitt 19.5.1:

Der Fehlerkonstruktor ist so konzipiert, dass er in Unterklassen unterteilt werden kann. Es kann als Wert einer Extended-Klausel einer Klassendeklaration verwendet werden. Unterklassenkonstruktoren, die das angegebene Fehlerverhalten erben möchten, sollten einen Superaufruf an den Fehlerkonstruktor enthalten, um Unterklasseninstanzen zu initialisieren.

Derzeit würde dies zu dem Fehler "Eine Klasse kann nur eine andere Klasse erweitern" führen:

class NotImplementedError extends Error {
    constructor() {
        super("Not Implemented");
    }
}
Bug ES6 Fixed

Hilfreichster Kommentar

Sogar es gibt heutzutage die Möglichkeit, die Klasse Error Ich habe immer noch Unannehmlichkeiten damit auf dem Knoten. Zum Beispiel habe ich Probleme mit "error.stack". Wenn ich throw new Error(...) mache, bekomme ich error.stack , aber wenn ich throw new CustomError() mache - mache ich nicht. Um es zu lösen, musste ich diesen Trick anwenden:

export class HttpError extends Error {
    httpCode: number;
    message: string;

    constructor(httpCode: number, message?: string) {
        super();
        if (httpCode)
            this.httpCode = httpCode;
        if (message)
            this.message = message;

        this.stack = new Error().stack;
    }
}

Alle 14 Kommentare

Dies ist ein allgemeines Problem für jede js-Bibliothek, die Funktionen verfügbar macht, die als Unterklassen gedacht sind. Wenn meine Bibliothek den Foo-Konstruktor exportiert und erwartet, dass Instanzen von Unterklassen von Foo empfangen werden (nicht nur Objekte, die eine Foo-ähnliche Schnittstelle implementieren), gibt es derzeit keine Möglichkeit, dies zuzulassen.

Ich würde gerne sagen können

class Error;

Für eine solche Anweisung würde kein Javascript ausgegeben, sondern nur eine Aktualisierung der Symboltabelle von Typescript.

Für den allgemeinen Fall möchte ich in der Lage sein, den Hauptteil einer Klassendeklaration durch ein Semikolon zu ersetzen, das Dinge wie erlaubt

class Foo<X, Y> extends Bar<X> implements Baz<Y>;

Dies würde bedeuten, dass die Bibliothek bereits garantiert, dass Foo.prototype eine Instanz von Bar.prototype ist und die Baz-Schnittstelle implementiert.

@metaweta Ich denke, es kann einige Redundanzen machen:

interface Foo {
}
class Foo; // Foo is now a class!
interface Bar extends Foo {
}
class Bar extends Foo; // Two `extends Foo`s. 

interface X {
}
class X extends Foo; // Hmm?
interface Y extends Foo {
}
class Y; // Hmm?

Es hat noch einige Potenziale, da die unterklassifizierbaren Schnittstellen als offene Klassen funktionieren. # 819

@metaweta Ihr erster Fall sollte durch eine Umgebungsklassendeklaration behandelt werden. z.B

// myLib.d.ts
declare class Error {
}
// no code emitted here for this. 
// myfile.ts
// Error is subclassable,
class MyError extends Error {
}

Für den zweiten Fall würde ich ein anderes Vorschlagsproblem dafür einreichen.

Hier ist die vollständige Liste der "unterklassifizierbaren" Objekte aus der ES6-Spezifikation. Derzeit sind diese alle als Interface / Var-Paare definiert.

  • Objekt
  • Boolescher Wert
  • Error
  • NativeError
  • Nummer
  • Datum
  • String
  • RegExp
  • Array
  • TypedArray (Int8Array, Uint8Array, Int16Array, Uint16Array, Int32Array, Uint32Array, Float32Array, Float64Array und DataView)
  • Karte
  • einstellen
  • Schwache Karte
  • WeakSet
  • ArrayBuffer
  • Datenansicht
  • Versprechen

Probleme:

  • Sobald ein Typ als Klasse definiert ist, gibt es keine Möglichkeit, die Mitgliedsseite zu erweitern (das Erweitern der statischen Seite kann über ein Modul erfolgen).
  • Bestehende Erweiterungen auf der Mitgliederseite funktionieren nicht, da Sie eine Klasse nicht neu definieren können (Änderung ändern)
  • Es gibt keine Möglichkeit, Funktionsaufrufe für eine Klasse zu modellieren, z. B. Object, String, Boolean, Date, Number, RegExp, Error und Array. (wieder Veränderung brechen)

Mögliche Lösungen:

  • Ermöglichen Sie die Erweiterung eines beliebigen Typs mit einer Prototyp-Eigenschaft und einer Konstruktsignatur
  • Erlaube das Erweitern der Instanzseite einer Klasse (z. B. Modul Class.prototype {}, mit freundlicher Genehmigung von
  • Ermöglichen das Definieren von Aufrufsignaturen in einem Klassenkonstruktor

@mhegazy Danke, mir war das Konstrukt der Deklaration der Umgebungsklasse nicht bekannt.

Wenn ich die Error-Klasse auf diese Weise erweitere (unter Verwendung der Umgebungsklassendefinition), erzeugt das Auslösen des ExtendedError weder eine Stapelverfolgung noch eine ordnungsgemäße Zuweisung von Nachrichten.

declare class Error {
  constructor(message?: string);
}

class ExtendedError extends Error {
  constructor(message?: string) {
    super(message + " extended");
  }
}

//throw new Error("Test");
throw new ExtendedError("Test");

@unional das sieht nach einem Motorproblem aus. gemäß ES6-Spezifikation sollte es funktionieren. Motoren sollten diese Probleme beheben, nachdem ES6 ratifiziert wurde.

Ich habe es geschafft, es zum Laufen zu bringen. In meinem funktionierenden js-Code habe ich den Aufruf Error.captureStackTrace, aber ich nehme ihn heraus, wenn ich ihn in ts implementiere, weil die Fehlerdeklaration ihn nicht hat.

Es gibt einen Beispielcode:
Es ist eine C # -ähnliche Ausnahme, die eine innerException akzeptiert.

declare class Error {
    public name:string;
    public message:string;
    public stack:string;
    constructor(message?:string);
    static captureStackTrace(error: Error, constructorOpt: any);
}

class Exception extends Error {
    public innerException: Error;
    constructor(message?: string, innerException?: Error|string) {
        // Guard against throw Exception(...) usage.
        if (!(this instanceof Exception)) return new Exception(message, innerException);
        super();
        if (typeof Error.captureStackTrace === 'function') {
            //noinspection JSUnresolvedFunction
            Error.captureStackTrace(this, arguments.callee);
        }
        this.name = "Exception";
        if (innerException) {
            if (innerException instanceof Error) {
                this.innerException = innerException;
                this.message = message + ", innerException: " + this.innerException.message;
            }
            else if (typeof innerException === "string") {
                this.innerException = new Error(innerException);
                this.message = message + ", innerException: " + this.innerException.message;
            }
            else {
                this.innerException = innerException;
                this.message = message + ", innerException: " + this.innerException;
            }
        }
        else {
            this.message = message;
        }
    }
}

Behoben durch # 3516. Klassen können jetzt beliebige Ausdrücke von Konstruktorfunktionstypen erweitern.

Welche Version von TypeScript wird dieses Update ausgeliefert?
Ich habe die Release-Labels für 1.5.3 und 1.5.4 schnell überprüft und es scheint, dass sie noch nicht ausgeliefert wurden, nur für den Moment im Master.

BEARBEITEN:
Entschuldigung, im Moment haben wir bemerkt, dass der Fehler durch Meilenstein "TypeScript 1.6" markiert ist.

Danke für deine Arbeit!

@kostrse Sie können unsere nächtlichen Versionen von TypeScript 1.6 ausprobieren, npm install -g typescript@next ausführen.

Hallo,

Ich verwende Typescript 1.6.2 und meine lib.es6.d.ts zeigen Fehler (und Array), ...) als Schnittstelle nicht Klasse.
Ist das schon in 1.6.2 behoben?

Prost!

@jpsfs Das @ahejlsberg in https://github.com/Microsoft/TypeScript/issues/1168#issuecomment -112955503 angegeben, soll es Klassen ermöglichen, beliebige Ausdrücke von Konstruktorfunktionstypen zu erweitern.

Sogar es gibt heutzutage die Möglichkeit, die Klasse Error Ich habe immer noch Unannehmlichkeiten damit auf dem Knoten. Zum Beispiel habe ich Probleme mit "error.stack". Wenn ich throw new Error(...) mache, bekomme ich error.stack , aber wenn ich throw new CustomError() mache - mache ich nicht. Um es zu lösen, musste ich diesen Trick anwenden:

export class HttpError extends Error {
    httpCode: number;
    message: string;

    constructor(httpCode: number, message?: string) {
        super();
        if (httpCode)
            this.httpCode = httpCode;
        if (message)
            this.message = message;

        this.stack = new Error().stack;
    }
}
War diese Seite hilfreich?
0 / 5 - 0 Bewertungen