टाइपस्क्रिप्ट डेकोरेटर कैसे लागू करें?


207

टाइपस्क्रिप्ट 1.5 में अब डेकोरेटर हैं

क्या कोई डेकोरेटर को लागू करने के लिए उचित तरीके का प्रदर्शन करते हुए एक सरल उदाहरण प्रदान कर सकता है और यह वर्णन कर सकता है कि संभावित वैध डेकोरेटर हस्ताक्षरों में क्या तर्क हैं?

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;

इसके अतिरिक्त, क्या कोई सबसे अच्छा अभ्यास विचार है जिसे डेकोरेटर को लागू करते समय ध्यान में रखा जाना चाहिए?


खुद पर ध्यान दें :-) यदि आप @Injectableएक डेकोरेटर में इंजेक्ट करना चाहते हैं , तो संदर्भ दें
आनंद रॉकज़ डेस

मैं इस परियोजना के कई उदाहरणों पर एक नज़र डालने का सुझाव दूंगा। कई सज्जाकार हैं - कुछ बहुत सरल हैं और कुछ को समझने के लिए थोड़ा और मुश्किल हो सकता है: github.com/vlio20/utils-decorators
vlio20

जवाबों:


396

मैंने डेकोरेटर्स के साथ खेलना शुरू किया और किसी भी दस्तावेज के आने से पहले इसका फायदा उठाने का फैसला किया। अगर आपको कोई गलती दिखती है तो कृपया इसे संपादित करने के लिए स्वतंत्र महसूस करें।

सामान्य अंक

  • डेकोरेटर्स को तब बुलाया जाता है जब क्लास को घोषित किया जाता है-न कि जब किसी ऑब्जेक्ट को तत्काल किया जाता है।
  • एकाधिक सज्जाकार एक ही कक्षा / संपत्ति / विधि / पैरामीटर पर परिभाषित किए जा सकते हैं।
  • कंस्ट्रक्टरों पर डेकोरेटर की अनुमति नहीं है।

एक वैध डेकोरेटर होना चाहिए:

  1. डेकोरेटर प्रकारों में से एक के लिए निर्दिष्ट ( ClassDecorator | PropertyDecorator | MethodDecorator | ParameterDecorator)।
  2. एक मान लौटें (वर्ग सज्जाकार और विधि डेकोरेटर के मामले में) जो सजाए गए मूल्य के लिए जिम्मेदार है।

संदर्भ


विधि / औपचारिक एक्सेसरी डेकोरेटर

कार्यान्वयन पैरामीटर:

  • target: कक्षा का प्रोटोटाइप ( Object)।
  • propertyKey: विधि का नाम ( string! symbol)।
  • descriptor: एक TypedPropertyDescriptor- आप एक विवरणक के कुंजी के साथ अपरिचित हैं, तो मैं इसके बारे में में पढ़ने की सलाह देते हैं इस दस्तावेज़ पर Object.defineProperty(यह तीसरी पैरामीटर है)।

उदाहरण - बिना तर्क के

उपयोग:

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

कार्यान्वयन:

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

इनपुट:

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

आउटपुट:

विधि args हैं: ["परीक्षण"]

वापसी मूल्य है: संदेश - परीक्षण

टिप्पणियाँ:

  • डिस्क्रिप्टर का मान सेट करते समय तीर सिंटैक्स का उपयोग न करें। यदि आप करते हैं तो संदर्भ का thisउदाहरण नहीं होगा।
  • नए डिस्क्रिप्टर को वापस करके वर्तमान विवरण को ओवरराइट करने की तुलना में मूल विवरणक को संशोधित करना बेहतर है। यह आपको कई डेकोरेटर का उपयोग करने की अनुमति देता है जो एक अन्य डेकोरेटर द्वारा किए गए ओवरराइटिंग के बिना डिस्क्रिप्टर को संपादित करता है। ऐसा करने से आप उसी समय @enumerable(false)और जैसे कुछ का उपयोग कर @logसकते हैं (उदाहरण: खराब बनाम अच्छा )
  • उपयोगी : किस प्रकार के TypedPropertyDescriptorहस्ताक्षर ( विधि उदाहरण ) या एक्सेसर हस्ताक्षर ( एक्सेसर उदाहरण ) को सजाने वाले को रखा जा सकता है, इसे प्रतिबंधित करने के लिए प्रकार के तर्क का उपयोग किया जा सकता है।

उदाहरण - तर्क के साथ (डेकोरेटर फैक्टरी)

तर्कों का उपयोग करते समय, आपको डेकोरेटर के मापदंडों के साथ एक फ़ंक्शन की घोषणा करनी चाहिए फिर तर्कों के बिना उदाहरण के हस्ताक्षर के साथ एक फ़ंक्शन लौटाएं।

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

स्थैतिक विधि डेकोरेटर

कुछ मतभेदों के साथ विधि सज्जाकार के समान:

  • इसका targetपैरामीटर कंस्ट्रक्टर फ़ंक्शन ही है और प्रोटोटाइप नहीं है।
  • डिस्क्रिप्टर को कंस्ट्रक्टर फ़ंक्शन पर परिभाषित किया गया है न कि प्रोटोटाइप में।

क्लास डेकोरेटर

@isTestable
class MyClass {}

कार्यान्वयन पैरामीटर:

  • target: वर्ग डेकोरेटर ( TFunction extends Function) पर घोषित किया गया है ।

उदाहरण का उपयोग करें : मेटाडाटा एपीआई का उपयोग करके किसी वर्ग पर जानकारी संग्रहीत करने के लिए।


संपत्ति सजाने वाला

class MyClass {
    @serialize
    name: string;
}

कार्यान्वयन पैरामीटर:

  • target: कक्षा का प्रोटोटाइप ( Object)।
  • propertyKey: संपत्ति का नाम ( string| symbol)।

उदाहरण का उपयोग करें : एक @serialize("serializedName")सज्जाकार बनाना और संपत्ति के नाम को क्रमबद्ध करने के लिए गुणों की सूची में जोड़ना।


पैरामीटर डेकोरेटर

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

कार्यान्वयन पैरामीटर:

  • target: कक्षा का प्रोटोटाइप ( Functionऐसा लगता Functionहै कि अब काम नहीं करता है। आपको किसी भी वर्ग के भीतर सजावट का उपयोग करने के लिए उपयोग करना चाहिए anyया Objectअब यहां करना चाहिए । या उस वर्ग प्रकार को निर्दिष्ट करें जिसे आप इसे प्रतिबंधित करना चाहते हैं)
  • propertyKey: विधि का नाम ( string! symbol)।
  • parameterIndex: फ़ंक्शन के मापदंडों की सूची में पैरामीटर का सूचकांक ( number)।

सरल उदाहरण है

विस्तृत उदाहरण


क्या आप जानते हैं कि एक पैरामीटर डेकोरेटर उदाहरण कहां मिलेगा? मैं एक सफलता github.com/Microsoft/TypeScript/issues/… के
रेमो एच। जिनसेन

1
@OweRReLoaDeD मैंने पैरामीटर डेकोरेटर के तहत एक उदाहरण जोड़ा है जो सिर्फ यह बताता है कि डेकोरेटर के पास क्या है। मुझे यकीन नहीं है कि अगर यह उपयोगी है। मैं इस समय एक अच्छे उदाहरण के बारे में नहीं सोच सकता।
डेविड शेरेट

FYI करें मैंने इकट्ठा किया और इस जानकारी को github पर
ट्विक किया

--experimentalDecorators ध्वज को इस उदाहरण के लिए कार्य करने के लिए सेट किया जाना है
Trident D'Gao

मैं थोड़ा क्या करने के लिए के रूप में उलझन में हूँ targetया prototype of the classऔर keyसंदर्भित करता है, किसी को उस पर विस्तृत कृपया सकता है?
सतेज एस

8

एक महत्वपूर्ण बात जो मैं अन्य उत्तरों में नहीं देखता:

डेकोरेटर का कारखाना

यदि हम यह घोषणा करना चाहते हैं कि डेकोरेटर को एक घोषणा पर कैसे लागू किया जाता है, तो हम एक डेकोरेटर फैक्टरी लिख सकते हैं। एक डेकोरेटर फैक्ट्री केवल एक फ़ंक्शन है जो कि रनटाइम पर डेकोरेटर द्वारा कहे जाने वाले एक्सप्रेशन देता है।

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

टाइपस्क्रिप्ट हैंडबुक डेकोरेटर्स चैप्टर देखें


4
class Foo {
  @consoleLogger 
  Boo(name:string) { return "Hello, " + name }
}
  • लक्ष्य: उपरोक्त मामले में कक्षा का प्रोटोटाइप "फू"
  • प्रॉपर्टीकेई: उपरोक्त मामले में "बू" नामक विधि का नाम
  • डिस्क्रिप्टर: ऑब्जेक्ट का वर्णन => में वेल्यू प्रॉपर्टी होती है, जो बदले में स्वयं फंक्शन होती है: फंक्शन (नाम) {रिटर्न 'हैलो' + नाम; }

आप प्रत्येक कॉल में सांत्वना देने वाले कुछ को लागू कर सकते हैं:

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
यह कड़े संकलक सेटिंग्स के साथ संकलित करने के लिए एक कठिन कार्य है
PANDWood

वास्तव में, यह गलत है और संकलित नहीं किया जा सकता है, {{::}} रिटर्न के बाद सीधे कर्ली ब्रेसिज़ की आवश्यकता है। इसे आपके कोड के संभावित स्रोत से भी देखा जा सकता है - blog.wolksoftware.com/…
PANDWood
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.