Bir daktilo dekoratörü nasıl uygulanır?


207

TypeScript 1.5 artık dekoratörlere sahiptir .

Birisi bir dekoratörü uygulamanın doğru yolunu gösteren ve olası geçerli dekoratör imzalarındaki argümanların ne anlama geldiğini açıklayan basit bir örnek verebilir mi?

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;

Ayrıca, bir dekoratör uygulanırken akılda tutulması gereken en iyi uygulama hususları var mı?


Kendime not :-) @InjectableBir dekoratöre enjekte etmek istiyorsanız , bakın
Anand Rockzz

Bu projenin sahip olduğu birçok örneğe göz atmanızı öneririm. Birden fazla dekoratör var - bazıları çok basit ve bazıları anlamak biraz daha zor olabilir: github.com/vlio20/utils-decorators
vlio20

Yanıtlar:


396

Dekoratörler ile oynamaya son verdim ve herhangi bir belge çıkmadan önce bundan yararlanmak isteyen herkes için neyi anladığımı belgelemeye karar verdim. Herhangi bir hata görürseniz lütfen bunu düzenlemekten çekinmeyin.

Genel Hususlar

  • Dekoratörler, bir nesne başlatıldığında değil, sınıf bildirildiğinde çağrılır.
  • Aynı Sınıf / Özellik / Yöntem / Parametre üzerinde birden fazla dekoratör tanımlanabilir.
  • Dekoratörlere yapıcılara izin verilmez.

Geçerli bir dekoratör şöyle olmalıdır:

  1. Dekoratör türlerinden birine atanabilir ( ClassDecorator | PropertyDecorator | MethodDecorator | ParameterDecorator).
  2. Dekore edilmiş değere atanabilir bir değer (sınıf dekoratörleri ve yöntem dekoratörü olması durumunda) döndürün.

Referans


Yöntem / Formal Accessor Dekoratör

Uygulama parametreleri:

  • target: ( Object) Sınıfının prototipi .
  • propertyKey: Yöntemin adı ( string| symbol).
  • descriptorA TypedPropertyDescriptor- Bir açıklayıcısı anahtarIarýyIa bilginiz yoksa, ben bu konuda okumanızı tavsiye ederim bu belgelerin üzerinde Object.defineProperty(üçüncü parametre var).

Örnek - Bağımsız Değişken Olmadan

kullanın:

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

Uygulama:

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;
}

Giriş:

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

Çıktı:

Yöntem argümanları: ["test"]

Dönüş değeri: Mesaj - test

Notlar:

  • Tanımlayıcının değerini ayarlarken ok sözdizimini kullanmayın. Eğer yaparsanız, içeriği bağlam thisolmayacaktır.
  • Orijinal tanımlayıcıyı değiştirmek, yeni bir tanımlayıcı döndürerek geçerli olanın üzerine yazmaktan daha iyidir. Bu, başka bir dekoratörün yaptıklarının üzerine yazmadan tanımlayıcıyı düzenleyen birden fazla dekoratör kullanmanıza izin verir. Bunu yapmak , aynı anda @enumerable(false)ve benzeri bir şey kullanmanıza izin verir @log(Örnek: Kötü vs İyi )
  • Yararlı : Dekoratörünün tür argümanı , dekoratörün TypedPropertyDescriptorhangi yöntem imzalarını ( Yöntem Örneği ) veya erişimci imzalarını ( Erişimci Örneği ) koyabileceğini kısıtlamak için kullanılabilir .

Örnek - Bağımsız Değişkenlerle (Dekoratör Fabrikası)

Bağımsız değişkenleri kullanırken, dekoratör parametreleriyle bir işlev bildirmeniz ve ardından bağımsız değişken olmadan örneğin imzasını içeren bir işlev döndürmeniz gerekir.

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;
    };
}

Statik Yöntem Dekoratörü

Bazı farklılıkları olan bir yöntem dekoratörüne benzer:

  • Onun targetparametre yapıcı işlevi kendisi değil prototipidir.
  • Tanımlayıcı, prototipte değil yapıcı işlevinde tanımlanır.

Sınıf Dekoratörü

@isTestable
class MyClass {}

Uygulama parametresi:

  • target: Dekoratörün bildirildiği sınıf ( TFunction extends Function).

Örnek kullanım : Bir sınıfta bilgi depolamak için meta veri api'sini kullanma.


Emlak Dekoratörü

class MyClass {
    @serialize
    name: string;
}

Uygulama parametreleri:

  • target: ( Object) Sınıfının prototipi .
  • propertyKey: Özelliğin adı ( string| symbol).

Örnek kullanım : Bir @serialize("serializedName")dekoratör oluşturma ve özellik adını serileştirilecek özellikler listesine ekleme.


Parametre Dekoratörü

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

Uygulama parametreleri:

  • target: Sınıfın prototipi ( FunctionFunctionartık görünmüyor. Dekoratörü herhangi bir sınıfta kullanmak için şimdi anyveya Objectburada kullanmalısınız. Veya kısıtlamak istediğiniz sınıf türlerini belirtin)
  • propertyKey: Yöntemin adı ( string| symbol).
  • parameterIndex: İşlevin parametreleri listesindeki parametre dizini ( number).

Basit örnek

Ayrıntılı Örnek (ler)


Parameter Decorator örneğini nerede bulacağınızı biliyor musunuz? Başarısız bir tane uygulamaya çalışıyorum github.com/Microsoft/TypeScript/issues/…
Remo H. Jansen

1
@OweRReLoaDeD Parametre dekoratörünün altına, dekoratöre iletilenleri günlüğe kaydeden bir örnek ekledim. Bunun yararlı olup olmadığından emin değilim. Şu anda iyi bir örnek düşünemiyorum.
David Sherret

Bilginize bu bilgiyi github'da
arolson101

--experimentalDecorators bayrağı, bu örneğin çalışması için ayarlanmalıdır
Trident D'Gao

Biraz ne olduğuna karıştı targetya prototype of the classve keybirileri bu konuda ayrıntılı misiniz ifade eder?
Satej S

8

Diğer cevaplarda görmediğim önemli bir şey:

Dekoratör fabrikası

Bir dekoratörün bir deklarasyona nasıl uygulanacağını özelleştirmek istiyorsak, bir dekoratör fabrikası yazabiliriz. Bir Dekoratör Fabrikası, çalışma zamanında dekoratör tarafından çağrılacak ifadeyi döndüren bir işlevdir.

// 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 { ... }

TypeScript el kitabı Dekoratörleri bölümünü kontrol edin .


4
class Foo {
  @consoleLogger 
  Boo(name:string) { return "Hello, " + name }
}
  • hedef: Yukarıdaki durumda sınıfın prototipi "Foo"
  • propertyKey: yukarıdaki durumda "Boo" adı verilen yöntemin adı
  • descriptor: object => tanımı değer fonksiyonunu içerir, bu da fonksiyonun kendisidir: function (name) {return 'Hello' + name; }

Konsola yapılan her çağrıyı günlüğe kaydeden bir şey uygulayabilirsiniz:

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;
  }     
}

1
Sıkı derleyici ayarları ile bunu derlemek zor bir iştir
PandaWood

Aslında, bu yanlıştır ve derlenemez, dönüşten hemen sonra kıvırcık parantezlere ihtiyaç vardır {value: ...}. Bu, kodunuzun potansiyel bir kaynağından bile görülebilir - blog.wolksoftware.com/…
PandaWood
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.