Jak wdrożyć dekoratora maszynopisu?

TypeScript 1.5 ma teraz dekoratory .

Czy ktoś mógłby podać prosty przykład demonstrujący właściwy sposób implementacji dekoratora i opisać, co oznaczają argumenty w możliwych poprawnych podpisach dekoratora?

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;

Dodatkowo, czy istnieją jakieś względy dotyczące najlepszych praktyk, o których należy pamiętać podczas wdrażania dekoratora?

Author: David Sherret, 2015-04-21

3 answers

Skończyłem bawiąc się z dekoratorami i postanowiłem udokumentować to, co wymyśliłem dla każdego, kto chce to wykorzystać, zanim jakakolwiek dokumentacja wyjdzie na jaw. Jeśli zauważysz jakieś błędy, możesz to edytować.

Uwagi Ogólne

  • dekoratory są wywoływane, gdy klasa jest zadeklarowana-Nie, gdy obiekt jest instancją.
  • na tej samej klasie/właściwości/metodzie/parametrze można zdefiniować wiele dekoratorów.
  • dekoratorów nie wolno na konstruktorów.

Prawidłowym dekoratorem powinno być:

  1. przypisany do jednego z typów dekoratorów (ClassDecorator | PropertyDecorator | MethodDecorator | ParameterDecorator).
  2. Zwraca wartość (w przypadku dekoratorów klas i dekoratorów metod), którą można przypisać do dekoratorowanej wartości.

Numer referencyjny


Method / Formal Accessor Decorator

Parametry implementacji:

  • target: prototyp klasy (Object).
  • propertyKey: nazwa metody (string | symbol).
  • descriptor: A TypedPropertyDescriptor - jeśli nie jesteś zaznajomiony z kluczami deskryptora, polecam przeczytać o tym w tej dokumentacji na Object.defineProperty (jest to trzeci parametr).

Przykład-Bez Argumentów

Użycie:

class MyClass {
    @log
    myMethod(arg: string) { 
        return "Message -- " + arg;
    }
}

Realizacja:

function log(target: Object, propertyKey: string, descriptor: TypedPropertyDescriptor<any>) {
    const originalMethod = descriptor.value; // save a reference to the original method

    // NOTE: Do not use arrow syntax here. Use a function expression in 
    // order to use the correct value of `this` in this method (see notes below)
    descriptor.value = function(...args: any[]) {
        // pre
        console.log("The method args are: " + JSON.stringify(args));
        // run and store result
        const result = originalMethod.apply(this, args);
        // post
        console.log("The return value is: " + result);
        // return the result of the original method (or modify it before returning)
        return result;
    };

    return descriptor;
}

Wejście:

new MyClass().myMethod("testing");

Wyjście:

Metoda args to: ["testowanie"]

Wartość zwracana to: Message -- testing

Uwagi:

  • nie używaj składni strzałek przy ustawianiu wartości deskryptora. kontekst this nie będzie instancją, jeśli to zrobisz.
  • lepiej zmodyfikować oryginalny deskryptor, niż nadpisać bieżący, zwracając nowy deskryptor. Pozwala to na użycie wielu dekoratorów, które edytują deskryptor bez nadpisywania tego, co zrobił inny dekorator. Robiąc pozwala to na jednoczesne użycie czegoś w rodzaju @enumerable(false) i @log (przykład: Bad vs Good)
  • przydatne : argument type TypedPropertyDescriptor Może być użyty do ograniczenia, jakie podpisy metody (przykład metody ) lub podpisów accessora ( przykład Accessora ) można umieścić na dekoratorze.

Przykład - Z Argumentami (Fabryka Dekoratorów)

Używając argumentów, musisz zadeklarować funkcję za pomocą dekoratora parametry zwracają następnie funkcję z podpisem przykładu bez argumentów.

class MyClass {
    @enumerable(false)
    get prop() {
        return true;
    }
}

function enumerable(isEnumerable: boolean) {
    return (target: Object, propertyKey: string, descriptor: TypedPropertyDescriptor<any>) => {
        descriptor.enumerable = isEnumerable;
        return descriptor;
    };
}

Dekorator Metody Statycznej

Podobne do dekoratora metody z pewnymi różnicami:

  • jej parametrem target jest sama funkcja konstruktora, a nie prototyp.
  • deskryptor jest zdefiniowany na funkcji konstruktora, a nie na prototypie.

Dekorator Klasy

@isTestable
class MyClass {}

Realizacja parametr:

  • target: klasa, na której deklaruje się dekorator (TFunction extends Function).

Przykład użycia: używanie API metadanych do przechowywania informacji na klasie.


Dekorator Nieruchomości

class MyClass {
    @serialize
    name: string;
}

Parametry implementacji:

  • target: prototyp klasy (Object).
  • propertyKey: nazwa nieruchomości (string | symbol).

Przykład użycia : Tworzenie dekoratora @serialize("serializedName") i dodanie nazwy właściwości do listy właściwości do serializacji.


Dekorator Parametru

class MyClass {
    myMethod(@myDecorator myParameter: string) {}
}

Parametry implementacji:

  • target: prototyp klasy (Function-wygląda na to, że Function już nie działa. Powinieneś użyć any lub Object tutaj, aby użyć dekoratora w dowolnej klasie. Lub Określ typ (Y) klasy, do których chcesz ją ograniczyć)
  • propertyKey: nazwa metody (string | symbol).
  • parameterIndex: The indeks parametru na liście parametrów funkcji (number).

Prosty przykład

Szczegółowy przykład(y)

  • Jeśli chcesz dowiedzieć się więcej na ten temat, skontaktuj się z nami.]}
 343
Author: David Sherret,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2018-02-24 18:17:37

Jedna ważna rzecz, której nie widzę w innych odpowiedziach:

Fabryka dekoratorów

Jeśli chcemy dostosować sposób zastosowania dekoratora do deklaracji, możemy napisać fabrykę dekoratorów. Fabryka dekoratora jest po prostu funkcją, która zwraca wyrażenie, które zostanie wywołane przez dekoratora podczas wykonywania.

// This is a factory, returns one of ClassDecorator,
// PropertyDecorator, MethodDecorator, ParameterDecorator
function Entity(discriminator: string):  {
    return function(target) {
        // this is the decorator, in this case ClassDecorator.
    }
}

@Entity("cust")
export class MyCustomer { ... }

Sprawdź Podręcznik maszynopisu dekoratorzy Rozdział.

 8
Author: Ondra Žižka,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2016-11-02 03:34:08
class Foo {
  @consoleLogger 
  Boo(name:string) { return "Hello, " + name }
}
  • target: prototype of the class in the above case it ' s " Foo "
  • propertyKey: nazwa wywołanej metody, w powyższym przypadku "Boo"
  • deskryptor: opis obiektu = > zawiera właściwość value, która z kolei jest samą funkcją: function (name) { return 'Hello' + name;}

Można zaimplementować coś, co rejestruje każde wywołanie do konsoli:

function consoleLogger(target: Function, key:string, value:any) 
{
  return value: (...args: any[]) => 
  {
     var a = args.map(a => JSON.stringify(a)).join();
     var result = value.value.apply(this, args);
     var r = JSON.stringify(result);

     console.log('called method' + key + ' with args ' + a + ' returned result ' + r);

     return result;
  }     
}
 4
Author: Erik Lieben,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2015-04-23 13:42:32