टाइपस्क्रिप्ट में विभिन्न एनम वेरिएंट कैसे काम करते हैं?


116

टाइपस्क्रिप्ट में एनम को परिभाषित करने के विभिन्न तरीकों का एक समूह है:

enum Alpha { X, Y, Z }
const enum Beta { X, Y, Z }
declare enum Gamma { X, Y, Z }
declare const enum Delta { X, Y, Z }

यदि मैं Gammaरनटाइम से मान का उपयोग करने का प्रयास करता हूं, तो मुझे एक त्रुटि मिलती है क्योंकि Gammaपरिभाषित नहीं है, लेकिन यह उसके लिए मामला नहीं है Deltaया नहीं Alpha? यहां घोषणाओं पर क्या constया क्या declareमतलब है?

एक preserveConstEnumsसंकलक ध्वज भी है - यह इनसे कैसे बातचीत करता है?


जवाबों:


247

टाइपस्क्रिप्ट में चार अलग-अलग पहलू हैं जिनसे आपको अवगत होना चाहिए। सबसे पहले, कुछ परिभाषाएँ:

"लुकिंग ऑब्जेक्ट"

यदि आप यह एनम लिखते हैं:

enum Foo { X, Y }

टाइपस्क्रिप्ट निम्नलिखित वस्तु का उत्सर्जन करेगा:

var Foo;
(function (Foo) {
    Foo[Foo["X"] = 0] = "X";
    Foo[Foo["Y"] = 1] = "Y";
})(Foo || (Foo = {}));

मैं इसे लुकअप ऑब्जेक्ट के रूप में संदर्भित करूंगा । इसका उद्देश्य दोहरा है: से एक मानचित्रण के रूप में सेवा करने के लिए तार करने के लिए संख्या जब लेखन, जैसे Foo.Xया Foo['X'], और से एक मानचित्रण के रूप में सेवा करने के लिए संख्या के तार । यह रिवर्स मैपिंग डीबगिंग या लॉगिंग उद्देश्यों के लिए उपयोगी है - आपके पास अक्सर मूल्य होगा 0या 1संबंधित स्ट्रिंग प्राप्त करना चाहते हैं "X"या "Y"

"घोषित" या " परिवेश "

टाइपस्क्रिप्ट में, आप उन चीजों को "घोषित" कर सकते हैं जिनके बारे में कंपाइलर को पता होना चाहिए, लेकिन वास्तव में इसके लिए कोड का उत्सर्जन नहीं करना चाहिए। यह तब उपयोगी होता है जब आपके पास jQuery जैसी लाइब्रेरी होती है जो कुछ ऑब्जेक्ट (जैसे $) को परिभाषित करती है, जिसके बारे में आप टाइप जानकारी चाहते हैं, लेकिन कंपाइलर द्वारा बनाए गए किसी भी कोड की आवश्यकता नहीं है। युक्ति और अन्य दस्तावेज़ीकरण घोषणाओं को संदर्भित करता है जो इस तरह से "परिवेश" के संदर्भ में किया जाता है; यह ध्यान रखना महत्वपूर्ण है कि एक .d.tsफ़ाइल में सभी घोषणाएं "परिवेश" हैं (या तो एक स्पष्ट declareसंशोधक की आवश्यकता है या यह घोषणा के आधार पर निहित है)।

"इनलाइन किए जाने वाले"

प्रदर्शन और कोड आकार कारणों के लिए, अक्सर संकलित होने पर इसके संख्यात्मक समकक्ष द्वारा प्रतिस्थापित एक एनम सदस्य के संदर्भ के लिए बेहतर होता है:

enum Foo { X = 4 }
var y = Foo.X; // emits "var y = 4";

युक्ति इस प्रतिस्थापन को कॉल करती है, मैं इसे इनलाइनिंग कहूंगा क्योंकि यह कूलर लगता है। कभी-कभी आप यह नहीं चाहेंगे कि एनुम सदस्य, उदाहरण के लिए, क्योंकि एनम एपीआई एपीआई के भविष्य के संस्करण में बदल सकता है।


एनम, वे कैसे काम करते हैं?

आइए, एनम के प्रत्येक पहलू से इसे तोड़ते हैं। दुर्भाग्य से, इन चार खंडों में से प्रत्येक अन्य सभी से शर्तों का संदर्भ देने वाला है, इसलिए आपको संभवतः इस पूरी चीज़ को एक से अधिक बार पढ़ने की आवश्यकता होगी।

गणना बनाम गैर-संगणित (स्थिर)

इनम सदस्यों की गणना की जा सकती है या नहीं। कल्पना गैर-संगणित सदस्यों को निरंतर बुलाती है, लेकिन मैं उन्हें कॉन्स्टेबल के साथ भ्रम से बचने के लिए गैर-संगणित कहूंगा ।

एक गणना किए गए एनम सदस्य वह है जिसका मूल्य संकलन-समय पर ज्ञात नहीं है। गणना किए गए सदस्यों के संदर्भ बेशक, इनलाइन नहीं हो सकते। इसके विपरीत, एक गैर गणना enum सदस्य है जिसका मूल्य एक बार किया जाता है संकलन समय पर जाना जाता है। गैर-गणना वाले सदस्यों के संदर्भ हमेशा इनबिल्ड होते हैं।

कौन से एनुम सदस्यों की गणना की जाती है और कौन से गैर-गणना किए जाते हैं? constजैसा कि नाम से ही पता चलता है कि पहले एक एनम के सभी सदस्य निरंतर (यानी गैर-गणना वाले) हैं। एक नॉन-कॉस्ट एनम के लिए, यह इस बात पर निर्भर करता है कि आप एंबिएंट (डिक्लेयर) एनम या नॉन-एम्बिएंट एनम को देख रहे हैं या नहीं।

declare enum(यानी एंबियंट एनम) का एक सदस्य स्थिर होता है यदि और केवल यदि उसका इनिशियलाइज़र हो तो। अन्यथा, यह गणना की जाती है। ध्यान दें कि ए में declare enum, केवल संख्यात्मक आरंभिकों की अनुमति है। उदाहरण:

declare enum Foo {
    X, // Computed
    Y = 2, // Non-computed
    Z, // Computed! Not 3! Careful!
    Q = 1 + 1 // Error
}

अंत में, गैर-घोषित गैर-कॉन्स्टेबल एनमर्स के सदस्यों को हमेशा गणना करने के लिए माना जाता है। हालांकि, उनकी प्रारंभिक अभिव्यक्तियाँ स्थिरांक तक कम हो जाती हैं यदि वे संकलन-समय पर कम्प्यूटेशनल हैं। इसका मतलब है कि गैर-कॉन्स्टेबल एनम सदस्य कभी भी इनबिल्ड नहीं होते हैं (यह व्यवहार टाइपस्क्रिप्ट 1.5 में बदल गया है, नीचे "टाइपस्क्रिप्ट में परिवर्तन" देखें)

const बनाम नॉन-कास्ट

स्थिरांक

एक एनुम घोषणा में संशोधन हो सकता है const। यदि कोई एनम है const, तो सभी सदस्य अपने सदस्यों को संदर्भित करते हैं।

const enum Foo { A = 4 }
var x = Foo.A; // emitted as "var x = 4;", always

const enums संकलित होने पर लुकअप ऑब्जेक्ट का उत्पादन नहीं करता है। इस कारण से, Fooउपरोक्त कोड में एक सदस्य संदर्भ के हिस्से को छोड़कर संदर्भ के लिए एक त्रुटि है । Fooरनटाइम पर कोई ऑब्जेक्ट मौजूद नहीं होगा।

गैर स्थिरांक

यदि किसी एनुम घोषणा में संशोधन नहीं होता है, तो constइसके सदस्यों के संदर्भ केवल तभी अंतर्निर्मित होते हैं, यदि सदस्य गैर-गणना वाला होता है। एक नॉन-कॉस्ट, नॉन-डिक्लेयर एनम लुकिंग ऑब्जेक्ट उत्पन्न करेगा।

डिक्लेयर (परिवेश) बनाम नॉन-डिक्लेयर

एक महत्वपूर्ण प्रस्तावना यह है कि declareटाइपस्क्रिप्ट में एक बहुत विशिष्ट अर्थ है: यह ऑब्जेक्ट कहीं और मौजूद है । यह मौजूदा वस्तुओं का वर्णन करने के लिए है । declareउन वस्तुओं को परिभाषित करने का उपयोग करना जो वास्तव में मौजूद नहीं हैं, जिनके बुरे परिणाम हो सकते हैं; हम बाद में उन का पता लगाएंगे।

घोषित

declare enum लुकअप ऑब्जेक्ट का उत्सर्जन नहीं करेगा। यदि उन सदस्यों की गणना की जाती है (तो गणना बनाम गैर-गणना पर ऊपर देखें) इसके सदस्यों के संदर्भ उल्लिखित हैं।

यह ध्यान रखना महत्वपूर्ण है कि संदर्भ के अन्य रूपों declare enum को अनुमति दी जाती है, उदाहरण के लिए यह कोड एक संकलन त्रुटि नहीं है, लेकिन रनटाइम में विफल हो जाएगा :

// Note: Assume no other file has actually created a Foo var at runtime
declare enum Foo { Bar } 
var s = 'Bar';
var b = Foo[s]; // Fails

यह त्रुटि "कंपाइलर से झूठ मत बोलो" की श्रेणी में आती है। यदि आपके पास Fooरनटाइम नाम की कोई वस्तु नहीं है , तो न लिखें declare enum Foo!

A , declare const enumसे अलग नहीं है const enum, सिवाय --preserveConstEnums के मामले में (नीचे देखें)।

गैर घोषित

यदि यह नहीं है तो एक गैर-घोषित एनम एक लुकअप ऑब्जेक्ट का उत्पादन करता है const । ऊपर वर्णित है।

--preserveConstEnums ध्वज

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


आम त्रुटियों

सबसे आम गलती यह है कि declare enumजब एक नियमित रूप से enumया const enumअधिक उपयुक्त होगा का उपयोग करें । एक सामान्य रूप यह है:

module MyModule {
    // Claiming this enum exists with 'declare', but it doesn't...
    export declare enum Lies {
        Foo = 0,
        Bar = 1     
    }
    var x = Lies.Foo; // Depend on inlining
}

module SomeOtherCode {
    // x ends up as 'undefined' at runtime
    import x = MyModule.Lies;

    // Try to use lookup object, which ought to exist
    // runtime error, canot read property 0 of undefined
    console.log(x[x.Foo]);
}

सुनहरा नियम याद रखें: कभी declareचीजें हैं जो वास्तव में मौजूद नहीं हैconst enumयदि आप हमेशा इनलाइनिंग चाहते हैं, या enumयदि आप लुकिंग ऑब्जेक्ट चाहते हैं तो इसका उपयोग करें ।


टाइपस्क्रिप्ट में परिवर्तन

टाइपस्क्रिप्ट १.४ और १.५ के बीच, व्यवहार में एक परिवर्तन हुआ (देखें https://github.com/Microsoft/TypeScript/issues/2183 ) गैर-घोषित गैर- कॉन्स्टेबल सदस्यों के सभी सदस्यों को गणना के रूप में माना जाए, भले ही वे स्पष्ट रूप से एक शाब्दिक के साथ आरंभिक हैं। यह "बच्चे को अनप्लस" करता है, इसलिए बोलने के लिए, इनलाइनिंग व्यवहार को अधिक अनुमानित और अधिक स्वच्छ const enumरूप से अवधारणा को नियमित रूप से अलग करना enum। इस बदलाव से पहले, गैर-कास्ट एनमों के गैर-गणना वाले सदस्यों को अधिक आक्रामक रूप से इनलाइन किया गया था।


6
एक बहुत बढ़िया जवाब। इसने मेरे लिए बहुत सी बातें साफ कर दीं, न कि केवल बातें।
क्लार्क

1
काश, मैं आपको एक से अधिक बार वोट कर सकता ... उस ब्रेकिंग चेंज के बारे में नहीं जानता था। उचित अर्थ संस्करण में इसे प्रमुख संस्करण के लिए टक्कर माना जा सकता है: - /
mfeineis

विभिन्न enumप्रकार की एक बहुत ही उपयोगी तुलना , धन्यवाद!
मारियस शुल्ज

@ यह बहुत मददगार है, धन्यवाद! अब हमें केवल घोषित आवश्यकconst प्रकारों के लिए उचित उत्पादन करने के लिए वेब एसेंशियल 2015 की आवश्यकता है
स्टाइल जूल

19
यह उत्तर 1.4 में एक स्थिति की व्याख्या करते हुए महान विस्तार में जाता है, और फिर बहुत अंत में यह कहता है "लेकिन 1.5 ने यह सब बदल दिया और अब यह बहुत सरल है।" यह मानते हुए कि मैं चीजों को सही ढंग से समझता हूं, यह संगठन अधिक से अधिक अनुपयुक्त होने वाला है क्योंकि यह उत्तर पुराना हो जाता है: मैं दृढ़ता से सरल, वर्तमान स्थिति को पहले रखने की सलाह देता हूं , और उसके बाद ही कहता हूं "लेकिन अगर आप 1.4 या उससे पहले का उपयोग कर रहे हैं, तो चीजें थोड़ा और अधिक जटिल हैं। ”
KRyan

33

यहां कुछ चीजें चल रही हैं। चलो केस करके केस करते हैं।

enum

enum Cheese { Brie, Cheddar }

सबसे पहले, एक सादा पुरानी दुश्मनी। जब जावास्क्रिप्ट के लिए संकलित किया जाता है, तो यह एक लुकअप टेबल का उत्सर्जन करेगा।

लुकअप तालिका कुछ इस प्रकार है:

var Cheese;
(function (Cheese) {
    Cheese[Cheese["Brie"] = 0] = "Brie";
    Cheese[Cheese["Cheddar"] = 1] = "Cheddar";
})(Cheese || (Cheese = {}));

जब आप Cheese.Brieटाइपस्क्रिप्ट में होते हैं, तो यह Cheese.Brieजावास्क्रिप्ट में निकलता है जो 0. Cheese[0]का Cheese[0]मूल्यांकन करता है और वास्तव में मूल्यांकन करता है "Brie"

const enum

const enum Bread { Rye, Wheat }

कोई भी कोड वास्तव में इसके लिए उत्सर्जित नहीं होता है! इसके मूल्य अन्तर्निहित हैं। निम्नलिखित मान को केवल जावास्क्रिप्ट में 0 से बाहर निकालें:

Bread.Rye
Bread['Rye']

const enumप्रदर्शन कारणों से उपयोगी हो सकता है।

लेकिन इससे क्या Bread[0]? यह रनटाइम पर त्रुटि करेगा और आपके कंपाइलर को इसे पकड़ना चाहिए। लुकअप टेबल नहीं है और कंपाइलर यहां इनलाइन नहीं करता है।

ध्यान दें कि उपरोक्त मामले में, --preserveConstEnums ध्वज ब्रेड को लुकअप टेबल उत्सर्जित करेगा। इसके मूल्य अभी भी इनलेटेड होंगे।

घोषणा करें

के अन्य उपयोगों के साथ declare, declareबिना किसी कोड का उत्सर्जन करता है और आपको वास्तविक कोड को कहीं और परिभाषित करने की अपेक्षा करता है। यह कोई लुकअप तालिका नहीं बनाता है:

declare enum Wine { Red, Wine }

Wine.Red का उत्सर्जन करता है Wine.Redजावास्क्रिप्ट में है, लेकिन संदर्भ के लिए कोई भी वाइन लुकिंग टेबल नहीं होगी, इसलिए यह एक त्रुटि है जब तक कि आपने इसे कहीं और परिभाषित नहीं किया है।

घोषित करें

यह कोई लुकअप तालिका नहीं बनाता है:

declare const enum Fruit { Apple, Pear }

लेकिन यह इनलाइन करता है! Fruit.Appleउत्सर्जन करता है 0. लेकिन फिर Fruit[0]से रनटाइम पर त्रुटि करेगा क्योंकि यह इनबिल्ड नहीं है और लुकअप टेबल नहीं है।

मैंने इसे इस खेल के मैदान में लिखा है । मैं यह समझने के लिए वहां खेलने की सलाह देता हूं कि टाइपस्क्रिप्ट किस जावास्क्रिप्ट का उत्सर्जन करता है।


1
मैं इस उत्तर को अपडेट करने की सलाह देता हूं: टाइपस्क्रिप्ट 3.3.3 के अनुसार, Bread[0]संकलक त्रुटि फेंकता है: "एक
छारवे

1
हम्म ... क्या वह उत्तर जो कहता है उससे भिन्न है? "लेकिन ब्रेड [0] के बारे में क्या? यह रनटाइम में त्रुटि करेगा और आपके कंपाइलर को इसे पकड़ना चाहिए। कोई लुकअप टेबल नहीं है और कंपाइलर यहां इनलाइन नहीं करता है।"
कैट
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.