Typescript: Dekorateure

Erstellt am 7. März 2015  ·  139Kommentare  ·  Quelle: microsoft/TypeScript

ES7-Vorschlag

Den ES7-Vorschlag für Dekorateure finden Sie hier: https://github.com/wycats/javascript-decorators
Als Grundlage für diesen Vorschlag dient der ES7-Vorschlag. Nachfolgend finden Sie Hinweise, wie das Typensystem

Dekorateur-Ziele:

Klassenkonstruktor

@F("color")
<strong i="12">@G</strong>
class Foo {
}

Deszuckers zu:

var Foo = (function () {
    function Foo() {
    }
    Foo = __decorate([F("color"), G], Foo);
    return Foo;
})();

Methoden

class Foo {
  @F(color)
  <strong i="19">@G</strong>
  bar() { }
}

Deszuckers zu:

var Foo = (function () {
    function Foo() {
    }
    Foo.prototype.bar = function () {
    };
    Object.defineProperty(Foo.prototype, "bar", __decorate([F(color), G], Foo.prototype, "bar", Object.getOwnPropertyDescriptor(Foo.prototype, "bar")));
    return Foo;
})();

Statische Methode

class Foo {
    @F("color")
    <strong i="26">@G</strong>
    static sMethod() {}
}

Deszuckers zu:

var Foo = (function () {
    function Foo() {
    }
    Foo.sMethod = function () {
    };
    Object.defineProperty(Foo, "sMethod", __decorate([F("color"), G], Foo, "sMethod", Object.getOwnPropertyDescriptor(Foo, "sMethod")));
    return Foo;
})();

Eigenschaften

class Foo {
    @F("color")
    <strong i="33">@G</strong>
    prop: number;
}

Deszuckers zu:

var Foo = (function () {
    function Foo() {
    }
    __decorate([F("color"), G], Foo.prototype, "prop");
    return Foo;
})();

Formalparameter Methode/Accessor

class Foo {
    method(<strong i="40">@G</strong> a, @F("color") b) {}
}

Deszuckers zu:

var Foo = (function () {
    function Foo() {
    }
    Foo.prototype.method = function (a, b) {
    };
    __decorate([G], Foo.prototype, "method", 0);
    __decorate([F("color")], Foo.prototype, "method", 1);
    return Foo;
})();

Wobei __decorate definiert ist als:

var __decorate = this.__decorate || function (decorators, target, key, value) {
    var kind = typeof (arguments.length == 2 ? value = target : value);
    for (var i = decorators.length - 1; i >= 0; --i) {
        var decorator = decorators[i];
        switch (kind) {
            case "function": value = decorator(value) || value; break;
            case "number": decorator(target, key, value); break;
            case "undefined": decorator(target, key); break;
            case "object": value = decorator(target, key, value) || value; break;
        }
    }
    return value;
};

Unterschriften des Dekorateurs:

Ein gültiger Dekorateur sollte sein:

  1. Zuweisbar einem der Decorator-Typen (ClassDecorator | PropertyDecorator | MethodDecorator | ParameterDecorator) wie unten beschrieben.
  2. Gibt einen Wert zurück (im Fall von Klassendekoratoren und Methodendekoratoren), der dem dekorierten Wert zuweisbar ist.
declare type ClassDecorator = <TFunction extends Function>(target: TFunction) => TFunction | void;
declare type PropertyDecorator = (target: Object, propertyKey: string | symbol) => void;
declare type MethodDecorator = <T>(target: Object, propertyKey: string | symbol, descriptor: TypedPropertyDescriptor<T>) => TypedPropertyDescriptor<T> | void;
declare type ParameterDecorator = (target: Function, propertyKey: string | symbol, parameterIndex: number) => void;

Anmerkungen:

  • Das Dekorieren einer Funktionsdeklaration ist nicht zulässig, da dies das Anheben der Funktion an den Anfang des Gültigkeitsbereichs blockiert, was eine erhebliche Änderung in der Semantik darstellt.
  • Dekorationsfunktionsausdrücke und Pfeilfunktionen werden nicht unterstützt. Der gleiche Effekt kann durch Anwenden der Dekoratorfunktion als var x = dec(function () { });
  • Das Dekorieren von Funktionsformalparametern ist derzeit nicht Teil des ES7-Vorschlags.
  • Dekorateure sind bei der Ausrichtung auf ES3 nicht erlaubt
Committed ES Next Fixed Suggestion

Hilfreichster Kommentar

Funktionsdekoration ist natürlich notwendig.
Gibt es auch Pläne für die Dekoration anderer Objekte im Code?

Alle 139 Kommentare

Entschuldigen Sie, was ich von der Spezifikation verstehe, wir werden nicht in der Lage sein, Folgendes zu tun:

<strong i="6">@F</strong>
function test() {
}

Habe ich recht ?

Wie funktioniert die Typserialisierung mit Rest-Argumenten?

@F()  
class Foo {  
    constructor(...args: string[]) {  
    }  
}  

function F(<strong i="6">@paramterTypes</strong> types?: Function[]) {  
    return function (target) {  
        target.paramterTypes = types; // ???  
    }  
}

Die Verwendung von Dekoratoren scheint einfach genug, aber ich fand die Abschnitte über ihre Deklaration verwirrend. C.4 sagt, dass Dekoratoren mit @decorator annotiert werden müssen, aber kein einziges der Beispiele zeigt dies tatsächlich.

Sind Decorator-Fabriken als Klassen gedacht, die die in B gefundenen Schnittstellen implementieren?

Was ist die Regel zum Verfeinern der Interpretation von CoverMemberExpressionSquareBracketsAndComputedPropertyName?

Mir ist aufgefallen, dass viele der Eingaben an verschiedenen Stellen Function | Object , aber diese degenerieren zum Zeitpunkt der Typüberprüfung zu Objekt. Was ist der Grund, dort Function zu haben?

Ich bin nicht verrückt nach den Begriffen DecoratorFunction vs. DecoratorFactory. Ich würde viel lieber der Nomenklatur von Generatoren folgen, die Generator und GeneratorFunction hat. Mit diesem Schema würden wir DecoratorFunction in Decorator und DecoratorFactory in DecoratorFunction umbenennen.

Wofür sind [lookahead ≠ @] für die dekorierten Exporte? Können HoistableDeclaration und ClassDeclaration tatsächlich mit einem @ ?

Dies ist eine Kopie von #1557

Es ist nicht wirklich ein Betrug, da #1557 für ein anderes Design war. Dieses Problem ist für das Dekoratoren-Design, das jetzt implementiert wird.

Mein Fehler.

Für Decorator auf Funktionsausdruck könnten wir nicht etwas tun wie:

@F("color") <strong i="6">@G</strong> 
function myFunc() {
   doSomething();
}

umgewandelt in:

var _t = function() {
   doSomething();
}
_t = F("color")(_t = G(_t) || _t) || _t;  

function myFunc() {
  return _t.apply(this, arguments)
}

Es ist ein bisschen lästig, jede Funktion richtig einstellen zu müssen, wie zum Beispiel:

const myFunc = function () {}

Sie verlieren das Heben und function.name

Implementierung in PR #2399 hinzugefügt

Aktualisierungen: Angebot aktualisiert und Link zu den @wycats ES7-JavaScript-Dekoratoren hinzugefügt.

traurig, dass es zu einer einzigen Sache der Klasse wurde ...
Und was ist mit Ambient-Dekorateuren, die aus dem Anwendungsbereich herausgekommen sind?

@fdecampredon mit Ihrem Vorschlag für Funktionen scheint es, als ob Sie immer noch das Heben verlieren.

@JsonFreeman warum? wenn Sie _t am Anfang der Datei einfügen?

import something from 'something';

myFunc(something.something());

@F("color") <strong i="8">@G</strong> 
function myFunc() {
  doSomething()
}
import something from 'something';

var _t = function() {
   doSomething();
}
_t = F("color")(_t = G(_t) || _t) || _t;  

myFunc(something.something());

function myFunc() {
  return _t.apply(this, arguments)
}

Auch wenn mein Vorschlag viele Probleme hat, möchte ich ernsthaft in der Lage sein, Dekorateure für die Funktion zu verwenden (auch wenn ich eine variabel zugewiesene Funktion verwenden muss) und das Heben verlieren.
Ein Fall wie dieser scheint ein ziemlich guter Anwendungsfall für Dekorateure sowohl für Funktion als auch für Klasse zu sein (insbesondere in Verbindung mit Umgebungsdekoratoren, wenn sie noch im Geltungsbereich liegen).

@fdecampredon dies funktioniert im allgemeinen Fall nicht, da die Dekoratoren selbst Ausdrücke sind. z.B

myFunc();  // assumes function declaration is hoisted

var dec = (t) => t; // defininig a decorator

<strong i="7">@dec</strong>
function myFunc() {}

wenn Sie die Funktionsdeklaration und Anwendung des Dekorators hochziehen, dann brechen Sie den Dekorator. Wenn Sie nur die Funktionsdeklaration, aber nicht die Decorator-Anwendung hochziehen, können Sie die Funktion in einem nicht dekorierten Zustand beobachten. hier keine ansprechenden Lösungen.

Dies ist das gleiche Problem wie bei der Klassendeklarations-Erweiterungsklausel, die in ES6 ein Ausdruck ist. Das Ergebnis war nicht die Klassendeklaration, sondern nur das Symbol (ähnlich der var-Deklaration, aber nicht der Funktionsdeklaration)

Oups hat nicht daran gedacht, danke @mhegazy.
Warum jedoch der Funktionsteil den ursprünglichen @jonathandturner- Vorschlag vollständig aufgegeben

decorated function declarations cannot be hoisted to the containing scope

Das Verlieren des Hebens ist sicherlich ein Nachteil, aber ich finde es schädlich, es in ein reines Klassenmerkmal zu verwandeln, wenn es für andere Konstrukte einen Anwendungsfall hätte.

Sehen wir uns an, was der gewünschte Satz von Einschränkungen zu implizieren scheint:

  • dekorierte Funktion sollte gehisst werden
  • Funktion sollte dekoriert werden, sobald sie verfügbar ist - daher muss die Dekorationsanwendung hochgezogen werden
  • Decorator muss vor der Anwendung definiert werden - daher muss die Decorator-Definition selbst (oder alle Entitäten, auf die durch den Decorator-Ausdruck verwiesen wird) hochgezogen werden
  • Decorator-Ausdruck wird an der Stelle ausgewertet, an der er lexikalisch angewendet wird - daher kann die Anwendung nicht hochgezogen werden

Die einzige Auflösung, die ich dafür sehen kann, ist die folgende: Für Funktionsdekoratoren erlauben wir nur etwas in der Form @identifier . Wir erlauben keinen Ausdruck auf der linken Seite. Außerdem muss der Bezeichner ein direkter Verweis auf eine Funktionsdeklaration (einschließlich einer dekorierten Funktion) sein. Alle im Scope stattfindenden Funktionsdekorationen müssen am oberen Rand des Scopes ausgegeben werden, in der Reihenfolge, in der sie angewendet wurden.

Das Problem, die Heberegeln zu verletzen, ist, dass es überraschend ist. Wenn Sie für eine Weile Javascript schreiben, erwarten Sie, dass Funktionsdeklarationen verfügbar sind, bevor sie lexikalisch deklariert werden; Durch Hinzufügen eines scheinbar einfachen syntaktischen Markers (des Dekorators) wird diese grundlegende Natur der Funktionsdeklaration geändert.

Allerdings befindet sich der ES7-Vorschlag noch in der Anfangsphase, daher erwarte ich, dass er sich weiterentwickeln und erweitern wird; Daher ist es denkbar, dass der endgültige Vorschlag in irgendeiner Form Funktionen enthält.

Ich finde, sie sollten hochgezogen werden. Ich sage, dass die einzigen Dekorationen, die wir für Funktionen zulassen, Dekorationen sind, die definitiv selbst gehisst sind. Nämlich Bezeichner, die auf Funktionsdeklarationen verweisen.

Aber hier gibt es ein anderes Problem. Es ist eigentlich unmöglich, alle dekorierten Funktionen gleichzeitig zu heben und sicherzustellen, dass sie im unverzierten Zustand nicht beobachtet werden. Das Problem kann mit einem Decorator-Zyklus gesehen werden.

<strong i="7">@dec1</strong>
function dec2(target: Function) {
   // Do stuff
}

<strong i="8">@dec2</strong>
function dec1(target: Function) {
   // Do stuff
}

Auch wenn beide Funktionen hochgefahren sind, welche wird zuerst dekoriert? Wenn dec2 zuerst dekoriert wird, wird dec1 selbst nicht dekoriert, wenn es auf dec2 angewendet wird.

Wir müssten uns also zwischen den folgenden entscheiden:

  • Die Funktionen werden nicht angehoben
  • Die nicht dekorierten Funktionen werden hochgezogen, aber die Dekoratoranwendungen werden nicht mit ihnen hochgezogen.

Obwohl ich beides nicht mag, glaube ich nicht einmal, dass etwas anderes möglich ist.

Dies ist der JS-Vorschlag. die Engine wüsste also nicht, ob sich der Ausdruck auf eine Funktionsdeklaration bezieht oder nicht, mit der statischen Analyse könnten wir jedoch feststellen. bedenken Sie:

<strong i="6">@dec1</strong>
function dec2(target: Function) {
   // Do stuff
}

dec2 = undefined;

<strong i="7">@dec2</strong>
function dec1(target: Function) {
   // Do stuff
}

Pfui! Javascript wird nicht mehr einfacher. :-)

Es wäre einfacher, es nur für die Funktion _expressions_ zu aktivieren:

const foo = <strong i="6">@decorator</strong> () => {
    // ...   
}
const bar = <strong i="7">@decorator</strong> function() {
    // ...
}

Funktionsausdrücke oder Lambdas werden nicht hochgehoben.

Ist es möglich, Parameter wie diese in einem Dekorator in Typoskript 1.5.0-alpha zu haben?

@dec1({key1: value1, key2, value2})
function dec2(target: Function) {
   // Do stuff
}

Okay, egal, erstelle einfach eine Factory, die die Parameter übernimmt und die eigentliche Decorator-Funktion zurückgibt.

Zum Beispiel ein Klassen-Dekorator mit einem String-Parameter:

function decoratorWithString(param: string) { // Decorator factory
    return function(target) { // Actual decorator
        // Do stuff with target and string parameter
    }
}

// Usage
@decoratorWithString('foobar')
class Foo {

}

Schöne Grüße.

Ich versuche herauszufinden, wie man einen Decorator schreibt, der die im Konstruktor deklarierten Typen aufnimmt.

Hier ist ein Code, der veranschaulicht, was ich versuche, aber er ist fest verdrahtet. Ich brauche es, um auf die Konstruktordeklaration in Klasse D zu reagieren

class A {
  public message = "identity: class A";
}

class B {
  public message = "identity: class B";
}

<strong i="8">@decoTest</strong>
class D {
  static metadata:Array<Function> = [];
  constructor(a: A, b: B) {
  }
}
describe("decorators", function() {
  it("should inject constructor types", function() {
    var d = new D(new A(), new B());
    expect(D.metadata.length).toBe(2);
  });
});


function decoTest<T>(target: T, ...rest) {
  target["metadata"].push(A, B); // how do i get this based on constructor ???
  return target;
}

Ich befürchte, dass Typinformationen außerhalb des Umfangs der Decorator-Funktion liegen.
Sie können ParameterDecorator für jeden Parameter verwenden, um diese Art von Informationen hinzuzufügen.

Die Typisierung oder Implementierung für ParameterDecorator ist nicht ganz korrekt, von den Typisierungen ist das Ziel eine Funktion, aber bei der Verwendung von Typescript ist es das Prototypobjekt.
Ich muss den Zielparameter umwandeln, um den richtigen Typ zu erhalten:

function MyParameterDecorator (_target: Function, methodName: string, index: number) {
    const target = <InterfaceForMyUseCase><anyt>_target;
    // do stuff
}

Anstatt von:

function MyParameterDecorator (target: InterfaceForMyUseCase, methodName: string, index: number) {
    // do stuff
}

wow - die Regel der Parameter-Dekoratoren !!!!!!!!!!!!!!! siehe dieses Repository

Hier ist die Ausgabe der Tests:

LOG: 'injectMe:'
LOG: '  type: class A'
LOG: '  type: class B'
LOG: '  some key'

und der Code :

module ParameterDecorators {
  class A {
    static typeName:string = "type: class A";
    public instanceTypeName = "instance: class A";
  }

  class B {
    static typeName:string = "type: class B";
    public instanceTypeName = "instance: class B";
  }

  @injectTest(A, B, "some key")
  class C {
    static injectMe: Array<any> = [];
    constructor(a: A, b: B) {
    }
  }

  function injectTest(...rest) {
    return function(target): void {
      target["injectMe"] = rest;
    }
  }

  describe("decorators", function() {
    it("should inject dependency-injection keys", function() {
      var c = new C(new A(), new B());
      console.log("injectMe:");
      for (let parm of C.injectMe) {
        if (typeof(parm) === "function") {
          console.log("\t" + parm["typeName"]);
        } else {
          console.log("\t" + parm)
        }
      }
    });
  });
}

Ich habe einen Wrapper um Express erstellt (aber jedes Web-Framework könnte unterstützt werden, eine Adapterschnittstelle ist definiert) mit einer Decorator-basierten API: https://github.com/cybrown/web-decorators

Ich verwende ClassDecorator, ParameterDecorator und MethodDecorator.

@cybrown Ich habe gerade die Signatur für ParameterDecorator in #2635 aktualisiert, die sich jetzt im Master befindet.

@rbuckton Danke, ich werde das morgen in meinem Projekt aktualisieren.

Ist es möglich, den Namen des Parameters in einem ParameterDecorator zu haben?
Dies kann nützlich sein, da ich denke, dass der Name des Parameters die Hälfte des ersten Arguments eines ParameterDecorators sein könnte.
Darüber hinaus könnte es eine Lösung für die Parameternamen-Mangelung durch Minifier sein.

Ich wurde gebeten, dies zu tun:

@inject(A, B, "some key")
  class C {
    static injectMe: Array<any> = [];
    constructor(a: A, b: B) {
    }
  }

  function inject(...rest) {
    return function(target): void {
      target["inject"] = rest;
    }
  }

aber ohne die Schlüssel im Inject-Dekorator anzugeben, sondern die Dekoratoren die Klassenfunktionen des Konstruktors automatisch mit etwas in der Art aufnehmen zu lassen:

inject(<strong i="9">@parameterTypes</strong> types:Function[]){ ... }

Ich suche nach dem, was @jonathandturner hier beschreibt

@cmichaelgraham Ja, Laufzeittypinformationen (rtti wie in AtScript beschrieben) zu haben, wäre für diese Art der Verwendung großartig, selbst in einer vollständig getrennten Funktion wie Introspektion.

@cmichaelgraham Wir arbeiten an einem Vorschlag für ES7, eine Metadaten-API hinzuzufügen, die mit Dekorateuren zusammenarbeitet. #2589 fügt experimentelle Unterstützung für diesen Vorschlag hinzu, erfordert jedoch, dass ein separates Polyfill vorhanden ist, um die Metadaten zu lesen.

@rbuckton @cmichaelgraham Es gab ursprünglich ein Design für einen speziellen TypeScript-Dekorator, der dem Compiler mitteilen würde, Typen in den Dekorator zu injizieren, basierend auf dem zu dekorierenden Ziel. Ich denke, es war @parameterTypes Liege ich richtig, wenn ich denke, dass diese Funktion zugunsten der allgemeineren Metadaten-API entfernt wird? Wenn ja, würde ich das unterstützen.

Können Sie etwas über den Status in Bezug auf TypeScript sagen? Ist das für die Veröffentlichung von 1.5 geplant? Wenn ja, wie sehen die Compiler-Optionen aus? Es wäre nützlich, wenn der Compiler die Typmetadaten nur für Konstruktorsignaturen automatisch generiert.

@EisenbergEffect Decorators sind Teil von 1.5, Sie können es mit unserer neuesten Version 1.5-alpha verwenden .

Was die Unterstützung von Metadaten angeht. Das Design hat sich seit dem ursprünglichen Vorschlag für @paramtypes geändert. das neue Design verwendet den Reflect.metada-Vorschlag. Weitere Informationen zur Funktionsweise finden Sie unter #2589. auch @rbuckton hat einen Pollyfill, um die Metadaten hier zu konsumieren: https://github.com/rbuckton/reflectDecorators

@mhegazy Das weiß ich. Siehe hier: http://blog.durandal.io/2015/04/09/aurelia-update-with-decorators-ie9-and-more/ :smile:

Ich kenne auch den Metadatenvorschlag. Ich habe dazu Feedback gegeben. Ich wollte nur wissen, ob die ursprüngliche Idee von @parameterTypes entfernt wurde.

danke @EisenbergEffect fürs Teilen. :+1:

Jawohl. Das einzige Problem mit @paramtypes ist, dass die Ausgabe vom Typsystem gesteuert wird und globale Programminformationen erforderlich sind. Dies bricht Szenarien für die Einzelmodul-Transpilation (siehe # 2499). Die andere Möglichkeit bestand darin, es auf Call-Sites zu platzieren, was den Decorator-Benutzern anstelle der Autoren viel Arbeit auferlegt. Also gingen wir zurück zum Reißbrett und landeten stattdessen beim Reflect.metadata-Ansatz.

Wenn Sie sich auch frühere Versionen des Vorschlags angesehen haben, mussten wir aus dem gleichen Grund die Umgebungs-/Designzeit-Dekoratoren entfernen.

Danke fürs klarstellen. Das macht alles Sinn. Haben Sie eine Idee, ob der Reflect-basierte Ansatz in 1.5 landen wird?

Jawohl. es befindet sich derzeit im Master. es sollte in der nächsten Version verfügbar sein. es ist derzeit eine Opt-in-Funktion, die das experimentelle Flag --emitDecoratorMetadata ; und es fügt nur dekorierten Entitäten Metadaten hinzu.

"es fügt nur dekorierten Entitäten Metadaten hinzu" Können Sie diese Idee erweitern? Ist ein spezieller Dekorateur beteiligt? Oder fügt es es mit jedem Dekorateur zu etwas hinzu? Mit anderen Worten, wenn ein Entwickler den inject Dekorator von Aurelia verwendet, löst das dann den Compiler aus, die Metadaten zu generieren?

Wenn ein Entwickler den Inject-Decorator von Aurelia verwendet, löst das dann den Compiler aus, um die Metadaten zu generieren?

Jawohl.

was ich meinte ist:

<strong i="9">@inject</strong>
class Foo {
    constructor(a: number, b: string) {}
}

class Bar {
    constructor(a: number, b: string) {}
}

mit --emitDecoratorMetadata der Compiler Typmetadaten aus (dh Aufruf von reflect.metadata('desing:paramtypes', [Number, String]) ) für Foo aber _nicht_ Bar .

Das wird bei uns funktionieren. Wir werden die Unterstützung für die Reflect.metadata-API für unsere nächste Version, wahrscheinlich nächste Woche, vorbereiten. Danke für die Klarstellung!

also was wird dafür ausgegeben?

``` language=javascript
@injizieren
Klasse Foo {
Konstruktor(a: A, b: B) {}
}

Klasse Bar {
Konstruktor(a: Zahl, b: B) {}
}

would it be (for Foo):

``` javascript
reflect.metadata('desing:paramtypes', [A, B])

@cmichaelgraham Ja, das wird ungefähr generiert.

verrückt cool !!!!

Verwenden von gulp-typescript , um dieses Repository zu erstellen - (nette Arbeit @ivogabe)

gulpfile build-ts Befehl (beachten Sie die Option emitDecoratorMetadata ):

gulp.task('build-ts', function () {
    var tsResult = gulp.src([
        './views/*.ts',
        './typings/**/*.d.ts',
        './*.ts'
        ],
        {base: "."})
    .pipe(ts({
         typescript: require('typescript'),
         declarationFiles: false,
         noExternalResolve: true,
         target: "es5",
         module: "amd",
         emitDecoratorMetadata: true
    }));

    return merge([
        tsResult.dts.pipe(gulp.dest('.')),
        tsResult.js.pipe(gulp.dest('.'))
    ]);
});

wird app.ts

import {inject} from 'aurelia-framework';
import {Router} from 'aurelia-router';
import 'bootstrap';
import 'bootstrap/css/bootstrap.css!';

@inject(Router)
export class App {
  public router;
  constructor(router:Router) {
    this.router = router;
    this.router.configure(config => {
      config.title = 'Aurelia';
      config.map([
        { route: ['','welcome'],  moduleId: './welcome',      nav: true, title:'Welcome' },
        { route: 'flickr',        moduleId: './flickr',       nav: true },
        { route: 'child-router',  moduleId: './child-router', nav: true, title:'Child Router' }
      ]);
    });
  }
}

in app.js

var __decorate = this.__decorate || (typeof Reflect === "object" && Reflect.decorate) || function (decorators, target, key, desc) {
    switch (arguments.length) {
        case 2: return decorators.reduceRight(function(o, d) { return (d && d(o)) || o; }, target);
        case 3: return decorators.reduceRight(function(o, d) { return (d && d(target, key)), void 0; }, void 0);
        case 4: return decorators.reduceRight(function(o, d) { return (d && d(target, key, o)) || o; }, desc);
    }
};
var __metadata = this.__metadata || (typeof Reflect === "object" && Reflect.metadata) || function () { };
define(["require", "exports", 'aurelia-framework', 'aurelia-router', 'bootstrap', 'bootstrap/css/bootstrap.css!'], function (require, exports, aurelia_framework_1, aurelia_router_1, , ) {
    var App = (function () {
        function App(router) {
            this.router = router;
            this.router.configure(function (config) {
                config.title = 'Aurelia';
                config.map([
                    { route: ['', 'welcome'], moduleId: './welcome', nav: true, title: 'Welcome' },
                    { route: 'flickr', moduleId: './flickr', nav: true },
                    { route: 'child-router', moduleId: './child-router', nav: true, title: 'Child Router' }
                ]);
            });
        }
        App = __decorate([
            aurelia_framework_1.inject(aurelia_router_1.Router), 
            __metadata('design:paramtypes', [aurelia_router_1.Router])
        ], App);
        return App;
    })();
    exports.App = App;
});

Von besonderem Interesse sind die ausgegebenen Typmetadaten zu den Konstruktorparametern:

        App = __decorate([
            aurelia_framework_1.inject(aurelia_router_1.Router), 
            __metadata('design:paramtypes', [aurelia_router_1.Router])
        ], App);

Theoretisch könnten Sie also die Inject-Decorator-Funktion ändern, um die richtigen Klassen zu injizieren, ohne sie im Inject anzugeben, sondern stattdessen die Typen im Konstruktor anzugeben :)

@cmichaelgraham Wir können dies heute problemlos aktivieren, ohne das Framework zu ändern. Die DI-Bibliothek von Aurelia hat einen Hook container.addParameterInfoLocator . Sie übergeben ihm eine Funktion, die einen Typ annehmen und seine Abhängigkeiten zurückgeben kann. Für unser nächstes Release (nächste Woche) können wir dies jedoch zur Kernkonfiguration hinzufügen, sodass es für TypeScript-Entwickler einfach ist, es zu aktivieren. Ich hätte es in die Veröffentlichung dieser Woche aufgenommen, aber ich hatte nicht bemerkt, dass dies noch geändert wurde.

@EisenbergEffect genial !! :+1:

Ich habe Annotationen verwendet, um Eigenschaften eines Objekts als beobachtbar zu markieren, wodurch ein Primitiv in ein beobachtbares Objekt umgewandelt wird (für Interessierte https://github.com/mweststrate/MOBservable/commit/8cc7fc0e20c000db660037c8b5c9d944fe4155d9).

Insbesondere bei Eigenschaften fühlte es sich jedoch etwas unnatürlich an, dass die Annotationen auf den Prototyp und nicht auf den Konstruktor einer Klasse selbst angewendet werden. Dies bedeutet, dass es schwierig ist, entweder den Anfangswert oder sogar this . Die Umgehung dafür bestand darin, eine Eigenschaft zu erstellen und in ihrem Getter/Setter die eigentliche Logik auszuführen, die die Annotation ausführen sollte, da dann this und der Wert verfügbar sind.

Abgesehen davon, tolle Funktion!

Mit der Metadata Reflection API konnte ich eine einfache Eigenschaftsabhängigkeitsinjektion durchführen.
Sie können die erforderlichen Änderungen hier https://github.com/matjaz/property-DI/commit/2b4835e100b72d954b57d0e656ea524539ac17eb überprüfen

Sollte __metadata decorator in generiertem Code nicht zuerst von allen Decorators aufgerufen werden? Ich wollte einen einfachen Deokrator mit den Metadaten der Klasse erstellen, aber __metadata('design:paramtypes', [TYPES....]) wird als letztes aufgerufen, daher kann ich diese Daten nicht von Reflect abrufen

@ufon kannst du den Code teilen, den du dir

Klar, hier https://gist.github.com/ufon/5a2fa2481ac412117532

BEARBEITEN:
mein Fehler, es gibt einen anderen Fehler in meinem Code, ich kann auch nach der Klasseninitialisierung keine Typen abrufen

@ufon , ich bin mir nicht sicher, ob ich das Problem dann sehe. __metadata ist das letzte Element in der Decorator-Liste, das bedeutet, dass es als erstes ausgeführt wird und definitiv bevor inject ausgeführt wird.

    House = __decorate([
        inject_1.inject, 
        __metadata('design:paramtypes', [Floor, String])
    ], House);

wobei die __decorate Definition das Recht reduzieren verwendet, um die Dekoratoren in umgekehrter Reihenfolge auszuführen.

decorators.reduceRight(function(o, d) { return (d && d(o)) || o; }, target);

Dies entspricht der Spezifikation, dass Dekoratoren (als Ausdrücke) in der Reihenfolge der Deklaration ausgewertet, aber in umgekehrter Reihenfolge ausgeführt werden, sodass äußere Dekoratoren Ergebnisse von inneren Dekoratoren verarbeiten können.

Ich verstehe, sorry für den Ärger :) mein Fehler, es gibt einen anderen Fehler in meinem Code, kann auch nach der Initialisierung der Klasse keine Typen abrufen

wie fragst du nach ihnen? und haben Sie ein Pollyfill für Reflect.metadata?

zum Kern hinzugefügt...
Reflect.getMetadataKeys(Haus);
Dies führt zu einem leeren Array..

Ja, ich benötige das Paket 'reflect-metadata'

sieht so aus, als würde das Polyfill es verlieren, @rbuckton können Sie einen Blick darauf werfen.

Denke ich hab es...
require wird nach dem Hilfscode von typescript verschoben

var __metadata = this.__metadata || (typeof Reflect === "object" && Reflect.metadata) || function () { };
require('reflect-metadata');

zur Zeit wird also var __metadata initialisiert, Polyfill ist noch nicht geladen

Aber ich kann diese Anforderungen nicht vor dem generierten Code abrufen

BEARBEITEN: Das Wesentliche aktualisiert.. Wenn ein einzelnes Modul kompiliert wird, gibt es keine Möglichkeit, Polyfill zu laden, bevor __metadata aufgelöst wurde, da der generierte Code des Typskripts immer an den Anfang der Datei verschoben wird

ooh.. das ist ein größeres Problem. protokolliertes Problem #2811, um es zu verfolgen

Bis dahin benötigen Sie reflect-metadata in einem anderen Modul (Sie benötigen zwei Dateien).
siehe https://github.com/matjaz/property-DI/

Es scheint eine Inkonsistenz beim Aufruf zu geben, die die Entwicklung von Dekoratoren mit variablen/optionalen Parametern ziemlich schwierig macht. Als Beispiel:

<strong i="6">@F</strong>
prop: number;

@F()
prop: number;

Im ersteren Fall wird F mit den Parametern (target, propertyName, propertyDescriptor) aufgerufen, während im letzteren F mit den Parametern () aufgerufen wird und erfordert, dass Sie eine interne Funktion zurückgeben, die wiederum aufgerufen wird mit (target, propertyName und PropertyDescriptor).

Meine _Annahme_ wäre gewesen, dass sowohl @F als auch @F() gleichwertig wären und Sie daher beide

Dies ist besonders wichtig in folgenden Fällen:

<strong i="13">@F</strong>  // Performs some default behaviour.
prop: number;

@F({ option: true }) // Performs some configured behaviour.
prop: number;

Dies kann umgangen werden, indem einfach explizit @F() anstelle von @F aufgerufen wird, aber es ist leicht, diesen Fehler zu machen und dann verwirrt zu sein, wenn Ihr Dekorateur nicht funktioniert, obwohl es so aussieht, wie es aussieht.

In diesem Fall möchte ich Folgendes tun:

export function F(options?: any): PropertyDecorator {
    return (target, name) => {
        // do something.
    }
}

und fertig damit, aber stattdessen muss ich so etwas wie das folgende grobe Beispiel machen:

export function F(...args: any[]): any {
   var func = (target, name) => {
      // do something.
   }

   if (args.length === 1) return func;
   else if (args.length === 2) func(args[0], args[1]);
}

Was ein echter Schmerz ist.

@Tharaxis Dieses Verhalten ist @F und @F() sind nicht gleichwertig. @F ruft einen Dekorator F , während @F() einen Dekoratorfaktor F mit leerer Argumentliste aufruft und dann den resultierenden Dekorator anwendet.

Das Problem dabei ist, dass der Compiler die Signatur der Decorator-Factory F mit declare type PropertyDecorator = (target: Object, propertyKey: string | symbol) => void; die am Ende zuweisbar ist. Ich denke, wir brauchen hier eine stärkere Überprüfung, um sicherzustellen, dass wir diese Probleme lösen. Ich habe #3246 protokolliert, um dies zu beheben. Ich glaube, Sie tun das Richtige, und der Compiler sollte ungültige Verwendungen einer Decorator-Factory abfangen.

@mhegazy Ich bin mir nicht sicher, warum dies der Fall ist, gibt es jedoch einen Anwendungsfall, der ausschließt, dass @F und @F() gleichwertig sind, da beide letztendlich ein identischer Aufruf der Decorator-Funktion sind? , enthält nur einer von ihnen zufällig auch einen Aufruf einer äußeren Fabrikfunktion? Es scheint, dass hier das Prinzip des geringsten Erstaunens verletzt wird, da dies definitiv ein erstaunliches Verhalten ist und ich nicht sehe, warum es aus Sicht des API-Konsumenten zwei Möglichkeiten geben muss, einen Dekorateur aufzurufen.

Während ich das aus einer JS-Perspektive verstehe, gibt es einen Unterschied (in einer übergeben Sie die Referenz der F-Funktion und in der anderen führen Sie eine Funktion aus und übergeben dann die Referenz ihres Ergebnisses als Dekorator), bin ich nicht Sicher verstehe ich, warum man nicht einfach Dekoratoren haben kann, die als @F die die Factory implizit als eine mit einer leeren Parameterliste angleichen (und aufrufen)?

Der Compiler kann nicht erkennen, ob es sich um eine Factory oder einen Decorator handelt. wenn du diese Signatur hast:

declare function decoratorOrFactory (...args: any[]): any;

Ist dies ein Decorator oder eine Factory, soll sie mit leeren Argumenten aufgerufen werden oder nicht?

Der Compiler kann höchstens überprüfen, ob die Signatur übereinstimmt, wenn er auf die eine oder andere Weise aufgerufen wird, und Fehler ausgeben, wenn dies nicht der Fall ist.

@rbuckton hat einen Fix für #3246, der die Fälle

@mhegazy , was ich sage ist, dass die Verwendung eines Dekorators (dh @F oder @F() ) _immer_ ein Aufruf der Fabrikfunktion F sein sollte, die wiederum den entsprechenden Dekoratortyp zurückgibt. Es gibt keine Top-Level-Darstellung eines Dekorierers ohne eine kapselnde Fabrik.

Mit anderen Worten, die Signatur von F ist immer:

declare function F(...args: any[]): ClassDecorator | PropertyDecorator | MethodDecorator | ParameterDecorator;

Oder ein Äquivalent.

Dann wird der Aufruf @F @F() vom Compiler zum Äquivalent von @F als auch von @F() in irgendeiner Form mit der Fabriksignatur übereinstimmen müssen, löst dies das Problem der Aufrufverwirrung. Es gibt nur eine Möglichkeit, einen Dekorateur aufzurufen!

Folgendes gegeben:

declare function F(...args: any[]): PropertyDecorator {
    return (target, name) => {
        // do stuff.
    }
}

@F()
property: number;

und

declare function F(target, name) { // Matches PropertyDecorator
    // do stuff.
}

<strong i="22">@F</strong>
property: number;

Sie machen dasselbe, aber ich muss sie anders deklarieren! F zu deklarieren, damit es in beide Richtungen existieren kann, ist jedoch sehr umständlich und macht die Erstellung konsistenter Parameter und Dokumentation unmöglich. Sie entscheiden entweder, dass Ihr Dekorateur eine Fabrik verwenden muss oder nicht, und Ihr API-Nutzer muss diese Unterscheidung im Voraus kennen, um sie nutzen zu können.

@Tharaxis Wenn ich mir eine Bibliothek F bereitstellt, die als Dekorateur verwendet wird, erwarte ich absolut, dass @F und @F() unterschiedliche Dinge sind. So wie ich erwarten würde, dass C und C() unterschiedliche Dinge sind - das erste referenziert einen Wert, das zweite ruft einen Wert auf. @F wendet einen Dekorator an, @F() ruft eine Funktion auf, die einen Dekorator erstellt. Das sind nicht und sollten nicht die gleichen Operationen sein.

@DavidSouther gibt es einen triftigen Grund, warum dies nicht der Fall sein kann oder sollte? Es gibt keinen funktionalen Unterschied zwischen einem Aufruf von @F und @F() aber es gibt einen ziemlich großen Unterschied zwischen der Definition und der Dokumentation, und es erhöht die Komplexität des Aufrufs, die unnötig sein sollte.

Mein Vorschlag ist, dass es nicht zwei Möglichkeiten geben sollte, einen Dekorateur zu definieren, da dies nicht erforderlich ist, und dass es nicht erforderlich sein sollte, einen Dekorator auf mehr als eine Weise zu definieren.

Es gibt keinen funktionalen Unterschied zwischen einem Aufruf von @F und @F()

Ich denke, das ist der Hauptstreitpunkt. ich glaube nicht, dass dies wahr ist. F ist eine Referenz auf eine Funktion, F() ist eine Referenz auf den Rückgabewert von F, wenn sie mit einer leeren Argumentmenge aufgerufen wird (dh F.call(this, []) ); und dies sind zwei funktional und konzeptionell verschiedene Dinge.

@Tharaxis @F und @F() sind verschiedene Dinge. Es gibt Unterschiede in ihrer Verwendung, in ihrer Dokumentation und in ihrem absolut notwendigen Aufruf.

Lassen Sie mich es anders fragen: Warum muss jede Dekorateurfunktion eine Fabrik sein? Mit Ihrem Vorschlag wäre es unmöglich, einen einfachen Dekorateur zu haben.

@mhegazy, während ich zugeben werde, dass die letztere Option ( @F() ) zur Generierung eines funktionalen Abschlusses und zusätzlichen Overhead durch die Erstellung isolierter (und duplizierter) Dekoratorfunktionen und des @F Aufrufs führt im Wesentlichen gegen eine gemeinsame Dekoratorfunktionsreferenz funktioniert, würde ich sagen, dass die Isolierung durch Closure "sicherer" und konsistenter wäre, wodurch die Wahrscheinlichkeit von Überraschungen verringert wird.

@DavidSouther Ihre Frage hängt davon ab, was Sie als einfachen Dekorateur definieren. Nichts schließt einen einfachen Dekorateur in Form von:

declare function F(): PropertyDecorator {
    return (target, name) => {
        // do stuff
    }
}

Ich finde dies ziemlich minimal invasiv, da es nur 2 Zeilen mehr als die "einfache Syntax" ist und offensichtlicher definiert, was die verfügbaren Decorator-Aufrufe sind. Da es dann nur noch eine einzige Möglichkeit gibt, einen Dekorator zu definieren (und lassen Sie uns den Wert der Konsistenz nicht außer Acht lassen), werden Sie im Vergleich zur "einfachen Syntax" wahrscheinlich noch etwa 2 Sekunden brauchen, um dies herauszuholen.

Darüber hinaus ist folgendes Problem problematischer:

declare function F(options?: any): PropertyDecorator {
    return (target, name) => {
        // do stuff
    }
}

@F()
property1: number;

<strong i="15">@F</strong>
property2: number;

@F({ option: true })
property3: number;

Eines dieser Dinge ist nicht wie das andere. Die Verwendung von @F wird nicht funktionieren, obwohl es für bare Münze definitiv sollte. Denken Sie daran, tun Sie so, als wüsste ich nicht, wie die zugrunde liegende Deklaration von F aussieht, sondern wüsste stattdessen einfach, dass F existiert und einen optionalen Satz von Argumenten annehmen kann. Die Wahrscheinlichkeit, dass ich mit @F einen Fehler mache, ist nach dem derzeitigen Mechanismus nicht trivial. Dies stellt eine große Belastung für den Entwickler/Dokumentator dar, um sicherzustellen, dass der Verbraucher sich bewusst ist, dass @F nicht funktionieren wird (aber vielleicht wird es @C tun, weil es irgendwie "anders" ist).

Wenn ich einen "one size fits all"-Dekorateur haben möchte, bei dem sich der Verbraucher keine Sorgen machen muss, muss ich Folgendes tun:

declare function F(...args: any[]): any {
    var decorator = (target, name) => {
        // do stuff
    }

    // Heaven forbid your decorator formal parameter list also can take 2 parameters.
    return (args.length === 2) ? decorator(args[0], args[1]) : decorator;
}

Was ehrlich gesagt schrecklich ist. Außerdem verliere ich alle wertvollen Parameter-Intellisense gegen Dekorator F, da sie jetzt verallgemeinert werden muss.

Es spricht vieles dafür, dass Dekorateure einfach und konsistent zu definieren und zu dokumentieren sind. Machen wir uns nicht vor, zu glauben, dass jeder, der unseren Code konsumiert, das gleiche Wissen oder Verständnis hat wie wir, die wir den Code erstellt haben.

@Tharaxis Ich habe schon früh im Design für Dekorateure darüber nachgedacht, und ich verstehe Ihre spezifischen Punkte. Wie @mhegazy und @DavidSouther bereits in diesem Thread erwähnt haben,

Sie haben Recht, dass jeder Versuch, eine Funktion zu erstellen, die sowohl als Dekorator als auch als Dekoratorfabrik fungieren kann, etwas komplex sein kann. Im Allgemeinen kann es jedoch besser sein, eine bewährte Methode anzuwenden, um immer Dekorateurfabriken zu verwenden und Regeln an einen Linter zu liefern, um sicherzustellen, dass diese Vorgehensweise übernommen wird.

Außerdem habe ich gerade PR #3249 eingereicht, um Ihren früheren Punkt bezüglich Inkonsistenzen bei der Typprüfung für Dekorateure anzusprechen.

Hallo @rbuckton ,

Ich bin einer der nicht so versierten Benutzer von Dekorateuren, die von @Tharaxis erwähnt werden ...

Ich denke, die Frage, die ich hätte, ist, sollten Dekorateure mit Funktionen konsistent sein? Dh Im Fall von Funktionen macht es für mich Sinn, this.f versus this.f() zurückzugeben, da ich manchmal den Wert haben möchte, manchmal möchte ich das Ding, das den Wert erzeugt.

Bei Dekorateuren scheint es mir auf der Ebene der Sprachfeatures nicht ganz so klar zu sein. Ich möchte argumentieren, dass ich nur Decorator @F anwenden Factory- oder statische Methode implementiert wurde. Es sei denn, ich würde einige Fähigkeiten verlieren, die Dekorateure sonst hätten.

Ich entschuldige mich, wenn das Obige falsch informiert oder unwissend ist, ich bin relativ neu in der Javascript-Welt.

Danke & Prost

Ich muss diese Frage auch stellen - ist es sinnvoll, die syntaktische Parität mit Funktionen beizubehalten _if_ (und nur wenn) es keinen wirklichen Grund dafür gibt, außer "weil es mit Funktionen definiert ist". Ich sehe Decorators als ein völlig unabhängiges Feature zu Funktionen, die _zufällig_ mit ihnen definiert werden.

Im Moment verursacht die Tatsache, dass @F und @F() nicht identisch sind, die folgenden Probleme:

  • Entwickler, die den Unterschied zwischen @F und @F() verstehen müssen und warum dies manchmal funktioniert und warum dies manchmal nicht funktioniert (und die Notwendigkeit, @F vs. @F() könnte vollständig Framework-/Code-/Verwendungsspezifisch sein – daher vom Standpunkt des Verbrauchs aus inkonsistent: Stellen Sie sich vor, declare function A(params?: any): ParameterDecorator vs. declare function B(target, name) , @A wird nicht funktionieren, aber @A() wird, @B wird funktionieren, aber @B() wird nicht funktionieren, aber kognitiv sind sie dasselbe, eine Anwendung eines Dekorateurs ohne Argumente).
  • Das Beheben des Problems auf Codeebene erfordert eine umständliche Problemumgehung, die es ihnen unmöglich macht, die Aufrufsyntax des Decorators angemessen zu dokumentieren.
  • Das Refactoring ist etwas mehr Arbeit, wenn Sie einen 'Roh-Decorator' ( @F ) erstellt haben und jetzt einige Parameter hinzufügen möchten (aber aus Gründen der Abwärtskompatibilität optional). Nach der aktuellen Methode müssen alle diese @F Anforderungen jetzt auf @F() umgestaltet werden – einige davon im Code, auf den Sie möglicherweise keinen Zugriff haben – während Sie mit impliziten Aufrufen die Dinge am Laufen halten und die ändere deinen Code.

@Tharaxis Ich verstehe, dass die Unterscheidung zwischen @F und @F() eine hohe kognitive Belastung für die Verbraucher darstellen kann. Es hört sich jedoch so an, als ob Sie den ausgegebenen Code und damit das Verhalten ändern möchten, damit sich beide gleich verhalten. Dies würde wahrscheinlich dem ES7-Vorschlag für Dekorateure widersprechen, da es sehr unwahrscheinlich ist, dass die beiden Formulare gleich behandelt werden. Es wird nicht gut sein, wenn dies zu einer Divergenz führt.

Ein weiterer Punkt ist, dass die Verwirrung hier der Verwirrung zwischen der Übergabe einer Funktion als Callback und dem Aufruf der Funktion ähnelt. Wenn du das hättest

function callbackFactory() {
     return function(...args: any[]) { // do stuff };
}
function takesCallback(cb: (...args: any[]) => void) { }
takesCallback(callbackFactory());

Ein Benutzer kann verwirrt werden und takesCallback(callbackFactory) anrufen, ohne callbackFactory aufzurufen. Dies ist wirklich genau wie die Verwirrung, auf die Sie mit Dekorateuren hingewiesen haben, aber wenn die Sprache diese beiden Formen normalisiert, um dasselbe zu tun, würde die Ausdruckskraft verloren gehen. Manchmal ist die Unterscheidung wichtig und muss beibehalten werden. Ich bin mir nicht sicher, ob dies auch für Dekorateure üblich ist, aber theoretisch könnte es das sicherlich.

@JsonFreeman Ich verstehe dein Argument jedoch nicht wirklich. Sie sagen, dass Sie Dekoratoren nicht als eigene Einheit sinnvoll machen können, weil für Dekoratoren ein ES7-Vorschlag existiert, aber das ist die Sache, ES7-Dekoratoren sind genau das, ein _Vorschlag_, und soweit ich weiß, sind sie Sie basieren teilweise auf einer Kombination der von Google und Ihnen beschriebenen Dekorateurarbeiten, richtig? Es wurde noch nichts entschieden, wie sie funktionieren sollen, und es ist wahrscheinlich, dass der Vorschlag vor der Genehmigung _viele_ Iterationen durchlaufen wird die verschiedenen Parteien des Vorschlags hinter der Idee auf der Grundlage der in TS gewonnenen Erfahrungen? Dies wäre nicht das erste Mal, dass TS ein Feature implementiert hat, um es später umgestalten zu müssen, da die ES6-Spezifikation ein etwas anderes Verhalten erforderte. Sie werden dieses Problem _immer_ haben, solange Sie eine Spezifikation verfolgen, die noch nicht existiert und noch nicht vereinbart wurde.

Was Ihr Beispiel in Bezug auf Funktionen betrifft, so scheint es wieder einmal, dass wir Funktionen mit Dekoratoren verschmelzen, obwohl sie in Wirklichkeit völlig unterschiedliche Konstrukte sind (und sein sollten). Wenn ich mit einer Funktion in JavaScript arbeite, verstehe ich, wie Funktionen verwendet werden, und ich verstehe den Unterschied zwischen einer Funktionsreferenz und einem Funktionsaufruf (und wann ich vielleicht das eine oder das andere verwenden möchte) - und wenn ich etwas beschreibe Als Funktion gibt es keine Verwirrung hinsichtlich der Verwendungsmöglichkeiten. Dekoratoren sind _keine_ Funktionen und sollten das Verhalten von Funktionen nicht erben. Funktionen sind lediglich die Methode, nach der Dekoratoren konstruiert werden. Das ist so, als würde man sagen, dass Klassen Funktionen sind, was nicht der Fall ist. Funktionen sind lediglich die Art und Weise, wie wir Klassen vor ES6 beschreiben, weil es keine offensichtlichere Methode gab. Klassen übernehmen nicht die Eigenschaften von Funktionen, Sie können Klassen nicht wie Funktionen aufrufen, aber Sie deklarieren sie mit Funktionen vor ES6.

Unabhängig davon habe ich das Gefühl, dass ich meine Argumente zu diesem Thema erschöpft habe. Ich muss nur um die Auswahl herum arbeiten. In meinem eigenen Code werde ich aus Gründen der Konsistenz immer Fabriken verwenden. Ich glaube immer noch, dass die buchstäbliche Behandlung von Dekorateuren als Funktionen in der Zukunft wahrscheinlich viel Frustration verursachen wird, aber das bin nur ich.

@mhegazy Dies ist das gleiche Problem, das ich im @jonathandturner- Dekorator-Repo hier https://github.com/jonathandturner/decorators/issues/16 Ich denke, darüber sollte man nachdenken. So wie es aussieht, erwarte ich, dass diese Designentscheidung Gegenstand vieler Blog-Posts mit dem Titel "ES7 Decorator Pitfalls" ist.

@Tharaxis , ich stimme Ihrem ersten Punkt zu. Ich denke nicht, dass ein ES7-Vorschlag uns daran hindern sollte, etwas gut zu entwerfen, und unser Design ist in der Tat sowieso einer der Vorläufer von ES7.

Was das Argument "Dekorateure sind keine Funktionen" angeht, wenn ich mich nicht irre, diskutieren wir nicht über Dekorateure selbst, sondern über Dekorateurfabriken. Und ungeachtet jeglicher Unterscheidung zwischen Dekorateuren und Funktionen denke ich, dass Dekoratorfabriken_ eigentlich nur Funktionen sind. Und es hört sich so an, als ob Sie den Compiler auffordern, automatisch einen Aufruf an eine Decorator-Factory zu generieren, was eine gewöhnliche Funktion ist. In diesem Fall hätte der Programmierer keine Möglichkeit, zwischen der Decorator-Factory und dem Decorator zu unterscheiden.

Das Schöne daran, Dekorateure als Funktionen zu behandeln, ist auch, dass ein Verbraucher sie als Dekorateur anwenden oder einfach nach Belieben aufrufen kann.

@JsonFreeman das Argument dreht sich um die aktuelle Situation, in der es zwei Möglichkeiten gibt, einen Dekorator sowohl zu beschreiben als auch aufzurufen, eine über eine

Was ich frage ist ja, könnten wir den Compiler nicht einfach @F in das Äquivalent von @F() umwandeln lassen und die Typüberprüfung eine Argumentliste von 0...n Parametern erfordern, wenn die Syntax ohne Klammern. Vielleicht könnten Sie näher erläutern, was mit "...es gäbe keine Möglichkeit für den Programmierer, zwischen der Decorator-Factory und dem Decorator zu unterscheiden..." zu verstehen, da ich denke, dass es sehr leicht zu unterscheiden ist. Der Dekorateur ist immer die Antwort der Fabrik, und der Name der Fabrik ist der Name des Dekorateurs ... nicht so schwierig, oder verstehe ich das falsch?

Was Ihren letzten Punkt betrifft, der dem Verbraucher erlaubt, den Dekorateur einfach anzuwenden: Wenn gut beschrieben ist, dass alle Dekorateure Fabriken verwenden, ist es ziemlich einfach, einen Dekorateur selbst aufzurufen, Sie tun einfach <decorator name>(<argument list>)(target, name) Vergleich zu <decorator name>(target, name) als Beispiel. Denken Sie daran, dass die Vorgabe der Verwendung von Fabriken bedeuten würde, dass das erstere Beispiel immer funktioniert, während die Nichtvorgabe dazu führt, dass das letztere Beispiel manchmal nicht funktioniert und vollständig davon abhängt, wie der Dekorateur implementiert wird – ein Kopfzerbrechen, der nur darauf wartet, zu passieren.

Ich halte es für notwendig, darauf hinzuweisen, dass ich kein Problem mit Dekoratoren habe, die Funktionen verwenden, mein Problem ist jedoch, dass zwei Möglichkeiten, dasselbe zu beschreiben, zu Konsistenzproblemen führen. Vor allem, wenn diese beiden Möglichkeiten auch bedeuten, dass sich Ihre Aufrufmethode unterscheiden muss. Dies ist eine Disparität, die alles von der Umgestaltung bis zur Sprachkonsistenz behindert.

Das Refactoring-Problem, das ich vor ein paar Posts beschrieben habe, sollte bereits Grund genug sein, die aktuelle Methode zu überprüfen.

Hallo zusammen, nur letzte 1,5 Cent im Wert von einem normalen Benutzer, wie es @Tharaxis gesagt hat.

Ich würde mich über den aktuellen Vorschlag freuen, wenn:
a) das Universum schreibt es vor.
b) Dekorateure verlieren wichtige Fähigkeiten, wenn die Dinge anders wären.

Letzteres ist natürlich ein Werturteil, dessen Antwort bis zu einem gewissen Grad von Ihrem Entwicklertyp abhängt (dh allgemeiner Benutzer / erfahrener Benutzer usw.). Ich gehöre zur ersten Kategorie und bin im Allgemeinen dünn auf mehrere Sprachen und Frameworks verteilt. Für mich beinhalten "wichtige Fähigkeiten" keine Flexibilität, um Dekorateure auf zwei verschiedene Arten zu schreiben. Beispiele für alle Kompromisse wären großartig, wenn es sie gäbe.

Im Idealfall wäre es großartig, wenn @F und @F() unabhängig von der Implementierung von Dekorateuren konsistent sein könnten, aber wenn nicht, würde ich es vorziehen, beim Schreiben von Dekorateuren auf die Verwendung von Fabriken beschränkt zu sein, anstatt Rechen im Gras vermeiden zu müssen wann immer ich sie benutze.

Danke & Prost

Es scheint, als ob diese Anfrage von der Idee abhängt, dass Decorator-Fabriken der kanonische Weg sind, Decorators zu verwenden, aber ich sehe nicht, wie dies dem Benutzer ermöglicht, Raw-Decorators zu definieren und zu verwenden. Wenn ich einen Dekorator F und die Anwendung @F als @F() , dann wird das Ergebnis des Aufrufs von F anstelle von F selbst als Dekorator verwendet. Schlagen Sie vor, dass wir irgendwo einen Fehler ausgeben, wenn jemand versucht, einen rohen Dekorateur anstelle einer Dekorateurfabrik anzuwenden?

Diese Idee fühlt sich an, als würde sie die natürliche Kompositionalität von Dekorateuren und Dekorateurfabriken umkehren. Dekorateure fühlen sich hier definitiv wie das primitive Konstrukt, wobei Dekorateurfabriken eine Abstraktionsschicht sind, die auf Dekorateuren aufgebaut ist. Sie sind nur ein Muster, mehr nicht. Wenn stattdessen die Dekorateur-Fabrik das kanonische Ding, das Primitive, würde, dann würden die Leute eine Reihe von Dekorateur-Fabriken definieren, die keine Argumente akzeptieren und einen flachen Dekorateur zurückgeben. Dies würde sich sehr albern anfühlen und würde die natürliche Intuition dessen, was als grundlegend und was als komplexer angesehen wird, umkehren.

Eine Sache, bei der ich bei Dekorateuren sehr vorsichtig bin, ist ein Übermaß an Magie. Ich persönlich werde nervös, wenn ich nicht verstehe, was der Code macht, und wenn der Compiler heimlich zusätzliche Aufrufe hinzufügt, die der Programmierer nicht geschrieben hat, fühlt sich das für mich wie zu viel Voodoo an.

Hallo @JsonFreeman ,

Wie ich bereits erwähnte, würde ich es immer vorziehen, wenn die Hässlichkeit beim Autor des Dekorateurs und nicht beim Benutzer liegt. Ich stimme jedoch zu, dass viele No-Arg-Fabriken eine Menge hässlich sind. Könnte ein Dekorateur verwendet werden, um dies zu beheben? Z.B

// wraps rawDecoratorMethod in a no-arg factory method.
<strong i="8">@Decorator</strong>
function rawDecoroatorMethod(target, name, descriptor) {...}
// looks like this one would be a no-op... so that feels not quite right unless there's other advantages.
<strong i="11">@DecoratorFactory</strong>
function decoroatorFactoryMethod(someArg) {...}

Sollte sich ein solcher Ansatz als sinnvoll erweisen, hat er den Vorteil, dass die Annotation den Zweck der Funktion dokumentiert. dh es schmückt.

Danke schön

Gute Soße, Dekorateure, die helfen, Dekorateure zu definieren, ein ernster Fall von Typeception.

@JsonFreeman Ich bin mir nicht sicher, ob Null-Param-Funktionen unbedingt hässlicher sind als eine Vielzahl von (Ziel-, Namens-) Funktionen an ihrer Stelle. Wenn überhaupt, bieten die Zero-Param-Funktionen ein Maß an Spezifität/Explizitheit, das der anderen Methode fehlt (da die Parameter im letzteren Fall nie mit dem Aufruf übereinstimmen) und darüber hinaus ein einziges Ziel für das Dokument bereitstellen einer unnötigen Inkonsistenz. Ich sehe auch eine Zurückhaltung, einen Weg aufgrund einer wahrgenommenen "möglichen" Komplexität zu gehen, aber ich würde sagen, angesichts der sehr realen "offensichtlichen" Komplexität, die die aktuelle Implementierung auf der Verbrauchsseite auferlegt, sollte man eher daneben liegen des Offensichtlichen als des Möglichen.

Die andere Option besteht darin, festzulegen, dass Dekoratoren, die als @F aufgerufen werden, entweder das Muster für ClassDecorator, MethodDecorator, PropertyDecorator oder ParameterDecorator ODER eine 0..n arg-Factory-Funktion abgleichen können, die ClassDecorator, MethodDecorator, PropertyDecorator zurückgibt oder ParameterDecorator. Ich würde jedoch das Gefühl haben, dass diese Implementierung andere Fehler verursachen würde (was ist, wenn Sie zwei widersprüchliche Funktionen haben, welche würde als bestmögliche Übereinstimmung angesehen werden?) und würde den Compiler nur unnötig komplex machen. Die einfache Umwandlung von @F Aufrufen in @F() wäre die einfachere Lösung und würde die verschiedenen genannten Probleme lösen.

Entschuldigung, um das klarzustellen, ich habe nicht behauptet, dass Ihre Lösung komplex ist. Ich meinte, dass das automatische Aufrufen des Decorators vor dem Anwenden undurchsichtig ist. Der eingefügte Aufruf ist für den Benutzer unsichtbar, und ich habe das Gefühl, dass viele Benutzer ihn nicht erwarten werden.

Ich denke auch, wie es derzeit aussieht, ist nicht komplex. Wie Sie betonen, erlaubt es Bibliotheksautoren, vage oder schwammig darüber zu sein, ob ihre Funktion als Dekorateur angewendet werden soll. Das werde ich dir gewähren. Aber eine gute Bibliothek macht es klar.

Was passiert in dem von Ihnen vorgeschlagenen Schema, bei dem Sie den Aufruf automatisch durchführen, wenn Sie einen beliebigen Dekorator-Ausdruck anstelle eines Bezeichners verwenden? Wird das auch aufgerufen?

Ich stimme zu, dass der implizite Aufruf, sofern nicht anders dokumentiert, wahrscheinlich überraschend sein wird, aber nur für diejenigen, die keine Konzepte wie C#-Attribute und dergleichen verwendet haben.

Könnten Sie näher erläutern, was Sie mit einem willkürlichen Dekorateur-Ausdruck meinen?

Gute Soße, Dekorateure, die helfen, Dekorateure zu definieren, ein ernster Fall von Typeception.

Fairer Anruf. Dient mir recht für einen schlecht durchdachten Kommentar am späten Nachmittag am Wochenende. Lektion gelernt. Internet.undo().

Mein Punkt ist, dass, wenn sich herausstellt, dass eine konsistente Aufruf-Site-Syntax die Verwendung von Factorys erfordert, ich damit mehr als zufrieden bin. Ich werde ohne Zweifel einen Dekorateur schreiben, um die Kesselplatte zu entfernen. Auch hier würde ich ein wenig Schmerzen beim Schreiben des Dekorateurs gegenüber wiederholten Schmerzen bevorzugen (wenn auch zu diesem Zeitpunkt potenzielle Schmerzen). Andere werden anderer Meinung sein.

Gibt es nicht auch das Problem mit der API-Erweiterung? Ein ohne Fabrik erstellter Dekorateur kann keine optionalen Parameter zu einem späteren Zeitpunkt hinzufügen, daher prognostiziere ich @F und @F2() für meine Zukunft.

Ich sehe auch nicht, dass Sie dieses Szenario erhalten würden, wenn Sie f und f() , da dies unterschiedliche Anwendungsfälle auf der Verbrauchsseite sind. Im Fall des Dekorators wende ich den Dekorator immer genau dann und dort auf das Ziel an, und es findet immer ein Methodenaufruf hinter den Kulissen statt.

Aber der springende Punkt ist für mich das Usability-Problem, wenn ich einen Dekorateur anwende, möchte ich nicht googeln müssen, um herauszufinden, wie der Autor ihn umgesetzt hat - mich interessiert das Verhalten, nicht die Verpackung.

Danke schön

Nur noch eine letzte Anmerkung zum Zusammenfassen und dann schweige ich. Kurz gesagt, das ist für mich wirklich nur eine Frage des Interaktionsdesigns. Ich bin sehr daran interessiert, dass Dekorateure von außen nach innen gestaltet werden und nicht umgekehrt.

Als UI/UX-Typ sehe ich das ziemlich oft. Ich habe mit talentierten Entwicklern zusammengearbeitet, die UI-Lösungen vorgeschlagen haben, die Benutzern schaden würden (ein Beispiel waren zwei Speichern-Buttons in einem Fall, um eine Transaktionskomplexität zu lösen – wieder ein kluger Kerl und guter Mensch). Ich denke nur, wenn man sich tief in die Logik komplexer Implementierungsdetails vertieft, kann es schwer sein, sein Wissen zu vergessen und es mit den Augen eines durchschnittlichen Benutzers zu sehen.

Wenn ich als Durchschnitt einfach falsch liege oder wenn die Komplexität das aktuelle Design erfordert, dann ist alles gut, ich muss nur mein Gehirn einschalten und lernen.

Danke schön

Wenn Ihr Dekorator kein Bezeichner, sondern ein anderer Ausdruck ist, würden Sie ihn dann automatisch aufrufen? Nehmen wir beispielsweise an, es sei ein Dekorator. ODER-verknüpft mit einem Fallback-Funktionsausdruck. Etwas wie:

@(F || function (target) { // default decorator behavior })
class C { }

Rufst du das dann automatisch auf? Dies würde zu einem falschen Ergebnis führen, da Sie am Ende den Standard-Decorator aufrufen, bevor er angewendet wird:

(F || function (target) { // default decorator behavior })()

Dies scheint nicht richtig zu sein.

@JsonFreeman , lässt die Decorator-Syntax

Das heißt, ich denke, die einzige Möglichkeit, wie es funktionieren würde, wäre, wenn der beliebige Ausdruck selbst die gleiche Fabriksyntax auswertet, also:

@(F || () => function(target) { /* default decorator behaviour */ })
class C { }

Ich stimme zu, dass dies dazu führt, dass Sie ein paar fummelige Bits um Ihren beliebigen Dekorator legen müssen, aber ich denke, man hat größere Probleme als das Hinzufügen von () => wenn Sie beliebige Ausdrücke als Dekoratoren verwenden.

HINWEIS: Ich meinte natürlich nicht, dass die Lambda-Syntax die einzige Möglichkeit wäre, sie zu deklarieren, aber () => ist etwas schöner als function() { return function(target) { /*...*/ } } .

Es tut mir wirklich leid, diese Debatte über die Syntax des Decorator-Aufrufs zu unterbrechen, aber könnte jemand die Reihenfolge der Aufrufe der Decorator-Ausdrücke offiziell klären? Insbesondere in Anlehnung an den Dekorator-Zieltyp, die Position des Zielelements in der ursprünglichen Quelle und die Reihenfolge der Dekoratoren auf einem einzelnen Ziel.

@billccn Die Dekoratoren werden von unten nach oben angewendet. Also im Original-Quellcode, wenn Sie hatten

class C {
    <strong i="7">@F</strong>
    <strong i="8">@G</strong>
    method() { }
}

Dies würde zuerst G auf die Methode anwenden und dann F auf das Ergebnis anwenden. Ist das, was Sie fragen?

Wie wäre es dazu:

<strong i="6">@A</strong>
class Clazz {
    <strong i="7">@B</strong>
    prop = 1;

    <strong i="8">@C</strong>
    method() {}

    <strong i="9">@D</strong>
    get prop() {return 1;}

    <strong i="10">@E</strong>
    method2() {}

    <strong i="11">@F</strong>
    prop2 = 1;
}

Sie folgen der Gültigkeitsbereichsreihenfolge, also zuerst alle Eigenschaften/Methoden in der Reihenfolge der Deklaration, dann die Klasse eins. dh B, C, D, E, F, A.

Sie können den generierten Code hier sehen .

Es scheint derzeit nicht möglich zu sein, Parametereigenschaften zu dekorieren. Können sie unterstützt werden?

Parametereigenschaften sind Dinge wie constructor(private prop: Type) , wo eine Eigenschaft (Feld) und ein Parameter zusammen deklariert werden. Das Problem ist, dass beide dekoriert werden können, sodass möglicherweise eine einfallsreiche Syntax erfunden werden muss.

Frage: Können die Dekoratoren verwendet werden, um das Verhalten einer einfachen Objektmethode zu ändern, wie...

var isCreatorUser = () => {  /* code that verifies if user is the creator */ },
    isAdminUser = () => { /* code that verifies if user is admin */ },

var routeConfig = {
    get() {},
    <strong i="7">@isCreatorUser</strong> patch() {},
    <strong i="8">@isAdminUser</strong> <strong i="9">@isCreatorUser</strong> delete() {}
};

... und wenn nicht, warum? Es wird wirklich nützlich sein.

Noch nicht, aber wir denken darüber nach. @rbuckton könnte mehr Details haben.

Darf ich fragen, warum der Dekorateur den dekorierten Ergebnistyp nicht vom dekorierten Typ ändern kann?

Beispielsweise kann MethodDecorator alternativ wie folgt vorgeschlagen werden:

declare type MethodDecorator = <T, R>(target: Object, propertyKey: string | symbol, descriptor: TypedPropertyDescriptor<T>) => TypedPropertyDescriptor<R> | void;

Decorator kann also flexibler verwendet werden und funktioniert ähnlich wie ein Makro. Der Typ der dekorierten Klasse / des Feldes kann entsprechend dem zurückgebenden Typ des Dekorators aktualisiert werden, so dass die Typsicherheit weiterhin erhalten bleibt.

Ich weiß, dass https://github.com/jonathandturner/brainstorming/blob/master/README.md#c4 -defining-a-decorator angegeben hat, dass der Rückgabetyp derselbe sein sollte. Aber type validity ist hier IMHO kein gültiges Argument. Erstens können Dekoratoren in JavaScript den Klassen-/Feldtyp ändern, sodass kein Kompatibilitätsproblem zwischen TS und JS besteht. Zweitens werden Dekoratoren statisch angewendet, der TS-Compiler kann den ursprünglichen Feldtyp und den dekorierten Feldtyp an der Definitionsstelle ermitteln.

@HerringtonDarkholme Ich würde sagen, dass dies in einem anderen Vorschlag verfolgt werden muss. Der Hauptgrund ist die Art und Weise, wie der Compiler über Typen argumentiert, durch Deklarationen. Sobald ein Typ deklariert ist, kann er sich nicht mehr ändern. Das Hinzufügen von Typ-Mutatoren in der Mischung bedeutet, dass wir diese auflösen müssen, da wir die Deklaration auflösen, die haarig werden kann.

@mhegazy Also , kann ich dies als Kompromiss für die Einfachheit der Implementierung und nicht als bewusstes Design verstehen?

Gemäß diesem Vorschlag stellen Dekoratoren die Fähigkeit zur Ausführung von Code zur Entwurfszeit wieder her, während sie eine deklarative Syntax beibehalten. Der folgende Code lautet also
gültig in zukünftigem JavaScript, aber nicht gültig in heutigem TypeScript (allerdings könnte dies eines Tages von TS akzeptiert werden, oder?).

function SafeCtor(target) {
  var safe = function(...args) {
    var instance = Object.create(target.prototype)
    target.call(instance, ...args)
    return instance
  }
  safe.prototype = target.prototype
  return safe
}

<strong i="10">@SafeCtor</strong>
class Snake {
  constructor(name) {
    this.name = name
  } 
}

var snake = Snake('python')
alert(snake instanceof Snake)

Ich weiß, dass die Chancen für die Unterstützung dieser Funktion gering sind. Aber ich möchte bestätigen, dass dies keine ähnliche Geschichte ist wie structural vs. nominal Tippen.

Die aktuelle Prüfung besteht darin, dass Sie von Ihrem Dekorateur etwas zurückgeben müssen, das dem Ziel zuweisbar ist. Der fehlende Teil ist, dass wir keine Ergänzungen zum Typ erfassen. Ich glaube nicht, dass wir eine Idee haben, wie das geht, aber wir haben uns auch nicht wirklich darüber Gedanken gemacht. Daher denke ich, dass es ein fairer Vorschlag ist, aber die Kosten für die Implementierung können sehr hoch sein.

Im Idealfall sollte der vom Dekorator zurückgegebene Typ mit dem Ziel identisch sein. Es ist nicht abzusehen, ob Sie den dekorierten Typ in einer Quell- oder Zielposition in einer Aufgabe verwenden.

Obwohl ich denke, dass wir wirklich den Rückgabetyp des Decorators und das Ziel zusammenführen sollten, wie bei einem Mixin.

Nur eine Idee mit zusätzlicher Funktionalität. Da die Ausgabe des TypeScript-Compilers JavaScript ist, gibt es einige ähnliche Sprachen für Java, wie Xtend

Sie können einige interessante Ideen zu diesem Projekt kennenlernen. Suchen Sie nach aktiven Annotationen "Aktive Annotationen ermöglichen Entwicklern die Teilnahme am Übersetzungsprozess von Xtend-Quellcode in Java-Code über eine Bibliothek"

Die Steuerung der TypeScript-Ausgabe über Dekoratoren wird ein wirklich nettes Feature sein!

Dekorateure für Schnittstellen sollten möglich sein.

@shumy Das Problem ist, dass Schnittstellen zur Laufzeit in keiner Weise vorhanden sind und Dekoratoren ausschließlich zur Laufzeit ausgeführt werden.

Gibt es einen Grund, Eigenschafts-Dekoratoren in Bezug auf den Rückgabewert anders zu behandeln als Methoden-Dekoratoren? (Babel nicht).

Was ich meine ist, dass, wenn ich die Funktionalität für eine Eigenschaft über den Decorator definieren möchte, ich Folgendes tun muss:

function decorator(proto, name) {
    Object.defineProperty(proto, name, { value: 42 });
}

class Foo {
    <strong i="7">@decorator</strong>
    a: number;
}

Während Babel verlangt, dass der Deskriptor zurückgegeben wird:

function decorator(proto, name) {
    return { value: 42 };
}

class Foo {
    <strong i="11">@decorator</strong>
    a;
}

Dies macht es schwierig, einen Decorator in TypeScript zu schreiben, der Teil einer Bibliothek ist, die von Babel verwendet wird.

Ich würde vorschlagen, dass ein Property-Decorator genauso behandelt wird wie ein Methoden-Decorator, und der Rückgabewert des Decorators sollte über Object.defineProperty angewendet werden. Dies würde auch mehrere Dekoratoren auf einer einzigen Eigenschaft ermöglichen, genau wie bei einer Methode.

Eine Problemumgehung für den Moment (für Babel-Kompatibilität) besteht darin, den Deskriptor vom Dekorator zusätzlich zum Festlegen der Eigenschaft zurückzugeben, aber das scheint unnötig verschwenderisch zu sein:

function decorator(proto, name) {
    var d = { value: 42 };
    Object.defineProperty(proto, name, d);
    return d;
}

@mhegazy hast du zufällig einen Einblick in meinen obigen Kommentar?

@sccolbert In TypeScript haben wir uns entschieden, die Verwendung von Eigenschaftsdeskriptoren für Dekoratoren für Dateneigenschaften nicht

function decorator(proto, name) {
  return { value: new Point(0, 0); }
}

class Foo {
  <strong i="7">@decorator</strong>
  p: Point;

  setX(x) { this.p.x = 1; }
}

let a = new Foo();
let b = new Foo();
console.log(a.p.x); // 0
b.setX(10);
console.log(a.p.x); // 10 (!)

Es gibt einen Vorschlag für eine zukünftige Version von ES, eine "Initialisierungs"-Eigenschaft für den Deskriptor zu unterstützen, die während des Konstruktors ausgewertet wird. Auf diese Weise können Sie steuern, ob der Wert für den Prototyp festgelegt oder pro Instanz zugewiesen wird.

Im Moment können Sie diese Einschränkung umgehen, indem Sie Object.defineProperty direkt aufrufen, wie im Beispiel in Ihrem Kommentar. Wir werden weiterhin prüfen, ob diese Beschränkung in Zukunft gelockert wird.

@rbuckton danke! Sind Sie in Gesprächen mit Babel, um die gleiche Semantik zu erreichen? Ich denke, das ist das Wichtigste.

Warum wäre es sinnvoll, einen Dekorator zu verwenden, um einen Wert für eine bestimmte Instanz anzugeben? Ist das nicht der Eigenschaftsinitialisierer?

Mein Anwendungsfall ist komplizierter als das Beispiel. Ich verwende den Dekorator tatsächlich, um einen Getter zu definieren, der ein Objekt zurückgibt, das an den this Kontext der Eigenschaft gebunden ist, sodass Methoden für dieses Objekt Zugriff auf die Instanz haben, für die es definiert wurde.

Sie können sich dies so vorstellen, als würden Sie das Deskriptorprotokoll in Python emulieren, bei dem der Zugriff auf die Methode bar die für die Klasse Foo über die Instanz foo (dh foo.bar ) aufruft __get__ Methode der Funktion, die ein BoundMethod zurückgibt. Wenn dieses Objekt aufgerufen wird, wird die zugrunde liegende Funktion mit self als erstem Argument aufgerufen, in diesem Fall foo . Javascript hat dieses Konzept nicht, weshalb wir thisArg und function.bind überall anrufen müssen.

Für meinen Fall definiere ich keine Methoden, sondern ein typsicheres Signal. Hier ist der Dekorateur:
https://github.com/phosphorjs/phosphor-signaling/blob/1.0.1/src/index.ts#L144

Wenn auf die Eigenschaft zugegriffen wird, gibt sie eine Implementierung von ISignal die an den this Kontext des Eigentümers gebunden ist. Dadurch kann das Signal auf die Instanz zurückgreifen, auf der es definiert wurde:
https://github.com/phosphorjs/phosphor-signaling/blob/1.0.1/src/index.ts#L263

Tatsächlich verwende ich den Decorator als Abkürzung für dieses ausführliche Äquivalent:

class Foo {
  valueChanged: ISignal<number>;
}

defineSignal(Foo.prototype, 'valueChanged');

@rbuckton

Dekorateure sind bei der Ausrichtung auf ES3 nicht erlaubt

Wieso den? Ich kann nichts sehen, was die Verwendung von __decorate in ES3 verhindern würde.

Es verwendet den Eigenschaftsdeskriptor, der in ES3 nicht verfügbar ist
Le 4 sept. 2015 14:03 Uhr, "Koloto" [email protected] a écrit :

@rbuckton https://github.com/rbuckton

Dekorateure sind bei der Ausrichtung auf ES3 nicht erlaubt

Wieso den? Ich kann nichts sehen, was die Verwendung von __decorate in ES3 verhindern würde.


Antworten Sie direkt auf diese E-Mail oder zeigen Sie sie auf GitHub an
https://github.com/Microsoft/TypeScript/issues/2249#issuecomment -137717517
.

@sccolbert Das könnte mit ES6-Proxys besser sein als mit Eigenschaftsdekoratoren.

https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Proxy

@DavidSouther- Browser unterstützen noch keine Proxys :(

@cybrown Ja, es verwendet den Eigenschaftsdeskriptor für Methoden und Accessoren. Ich habe nur auf einfachen Eigenschaften (Felder ohne Accessoren) getestet. Aber es scheint, dass Dekoratoren in ES3 für Eigenschaften (ohne Accessoren) und Klassen zugelassen werden können. Es wäre hilfreich.

Und ein gefälschter Eigenschaftsdeskriptor kann für Methoden verwendet werden, die auf ES3 abzielen. Etwas wie { writable: true, enumerable: true, configurable: true } . Ich kann also keinen Grund sehen, ES3 nicht zu unterstützen.

@sccolbert Ich

@JsonFreeman Verbessertes _dieses_ Tippen klingt für einige meiner anderen Anwendungsfälle faszinierend. Hast du dazu noch mehr Infos?

Ich denke, die am weitesten entwickelte Diskussion über _dieses_ Tippen ist bei #3694.

Danke schön!

Fehler TS1207: Dekoratoren können nicht auf mehrere get/set-Accessoren mit demselben Namen angewendet werden.

<strong i="6">@get</strong>
public get myValue():any{...}

<strong i="7">@set</strong>
public set myValue(value:any){...}

Der obige Code ist nicht erlaubt, auch wenn es im Vergleich zu mehr Sinn macht

<strong i="11">@get</strong>
<strong i="12">@set</strong>
public get myValue():any{...}

public set myValue(value:any){...}

Getter und Setter werden in einem Aufruf von Obect.defineProperty definiert. Dies ist eher eine js-Eigenart, die Deklaration von set und get ist zwar getrennt, aber in Wirklichkeit die gleiche Eigenschaftsdeklaration. Die Fehlerprüfung im Compiler soll Benutzer warnen, wenn sie separat an sie denken; die Dekoratoren werden nur einmal auf den Eigenschaftsdeskriptor angewendet.

Ich frage mich nur, da der Compiler das Get und das Set mit demselben Namen erkennen und zu einer einzigen Object.defineProperty kombinieren kann, warum nicht auch den Decorator kombinieren?
oder vielleicht ein Options-Flag, um den Compiler anzuweisen, sie zu kombinieren und eine Warnmeldung zu hinterlassen, anstatt einen Fehler auszulösen.

Danke schön

@TakoLittle : Der Grund, warum wir dies heute nicht tun, liegt teilweise daran, wie Dekorateure zusammengesetzt sind. Dekorateure folgen den gleichen Prinzipien wie die mathematische Funktionskomposition, wobei (_f_ ∘ _g_)(_x_) zusammengesetzt ist als _f_(_g_(_x_)). Im gleichen Sinne kann man denken, dass:

<strong i="7">@F</strong>
<strong i="8">@G</strong>
class X {}

Ist ungefähr:

F(G(X))

Die Kompositionalität von Dekorateuren bricht zusammen, wenn Sie sowohl den Getter als auch den Setter dekorieren:

class C {
  <strong i="15">@F</strong>
  set X(value) {}

  <strong i="16">@G</strong>
  get X() {}
}

Wie komponieren F und G hier? Basiert es ausschließlich auf der Dokumentreihenfolge (dh F(G(X)) )? Ist jeder Satz von Dekoratoren für den Getter und den Setter diskret und wird dann in Dokumentreihenfolge ausgeführt (dh G(F(X)) )? Bedeuten get und set eine bestimmte Reihenfolge (dh ist get immer vor set oder umgekehrt)? Bis wir 100% sicher sind, dass der konsistenteste Ansatz, der die Benutzer nicht überrascht, oder einen gut dokumentierten Ansatz hat, der Teil des Decorators-Vorschlags mit mindestens Stufe 2 oder besserer Akzeptanz innerhalb der ECMA-262 ist, halten wir es für das Beste, hier restriktiver und fehleranfälliger sein, da es uns ermöglicht, diese Einschränkung zu einem späteren Zeitpunkt zu lockern, ohne eine Breaking Change einzuführen, die leicht unbemerkt bleiben und möglicherweise zu unerwartetem Verhalten zur Laufzeit führen könnte.

@rbuckton vielen Dank für die ausführliche Erklärung
TS-Team tolle Arbeit!! ^^d

Wo ist die Dokumentation dazu? und möchten Sie den Implementierungs-Commit verknüpfen?

Vielen Dank.

@mhegazy Wie ist der Stand der Implementierung der neuesten Version der Spezifikation. Ich verstehe, dass es da einige Änderungen gibt.

Dieses Problem verfolgte die ursprüngliche Version des Angebots. Da dies abgeschlossen ist, schließen wir dieses Thema. Für alle Aktualisierungen der Spezifikation werden wir neue Probleme protokollieren und alle wichtigen Änderungen skizzieren. Ich glaube nicht, dass der Vorschlag jetzt so weit ist, dass wir ihn aufgreifen können. Wir arbeiten eng mit @wycats an dem neuen Vorschlag zusammen.

@mhegazy Danke für das Update. Ich würde gerne informiert bleiben. Wenn Sie die neue Ausgabe für das Spezifikations-Update erstellen, verlinken Sie sie bitte hier, damit ich benachrichtigt und verfolgt werden kann. Die Aurelia-Community macht stark von Dekoratoren Gebrauch, und wir möchten das Update sowohl mit TypeScript als auch mit Babel synchronisieren. Nochmals vielen Dank für die großartige Arbeit, die das TS-Team leistet!

Funktionsdekoration ist natürlich notwendig.
Gibt es auch Pläne für die Dekoration anderer Objekte im Code?

War diese Seite hilfreich?
0 / 5 - 0 Bewertungen

Verwandte Themen

MartynasZilinskas picture MartynasZilinskas  ·  3Kommentare

CyrusNajmabadi picture CyrusNajmabadi  ·  3Kommentare

Antony-Jones picture Antony-Jones  ·  3Kommentare

dlaberge picture dlaberge  ·  3Kommentare

manekinekko picture manekinekko  ·  3Kommentare