टाइपस्क्रिप्ट युद्ध या Enum विजय

प्रागितिहास


डेढ़ साल पहले, हमारी कंपनी ने नई और अधिक फैशनेबल प्रौद्योगिकियों पर स्विच करने का फैसला किया। ऐसा करने के लिए, विशेषज्ञों का एक समूह बनाया गया था, जो यह था: तकनीकी स्टैक पर फैसला करना, इस स्टैक के आधार पर लिगेसी कोड के लिए एक पुल बनाना, और अंत में कुछ पुराने मॉड्यूल को नई पटरियों पर स्थानांतरित करना। मैं इस समूह में आने के लिए भाग्यशाली था। क्लाइंट कोड आधार कोड की लगभग एक मिलियन लाइनें है। हमने टाइपस्क्रिप्ट को भाषा के रूप में चुना। उन्होंने vue- क्लास-कंपोनेंट और IoC के साथ संयोजन के आधार पर GUI सब्सट्रेट बनाने का निर्णय लिया।

लेकिन कहानी इस बारे में नहीं है कि हमें कोड की विरासत से कैसे छुटकारा मिला, बल्कि एक छोटी सी घटना के बारे में जिसके परिणामस्वरूप ज्ञान का एक वास्तविक युद्ध हुआ। कौन परवाह करता है, बिल्ली में आपका स्वागत है।

समस्या से अवगत कराना


शुरुआत के कुछ महीने बाद, वर्किंग ग्रुप नए स्टैक के साथ सहज हो गया और पुराने कोड का हिस्सा उसमें ट्रांसफर करने में कामयाब रहा। आप यहां तक ​​कह सकते हैं कि मांस के कुछ महत्वपूर्ण द्रव्यमान थे जब आपको रुकने की ज़रूरत थी, एक सांस लें और देखें कि हमने क्या किया था।

जिन स्थानों पर आपको गहन अध्ययन की आवश्यकता है, जैसा कि आप जानते हैं, पर्याप्त थे। लेकिन सभी महत्वपूर्ण चीजों में, विडंबना यह है कि कुछ भी मुझे नहीं पकड़ा। डेवलपर के रूप में नहीं। लेकिन एक महत्वहीन, इसके विपरीत, सता रहा था। मुझे पागलपन की बात पर गुस्सा आ रहा था कि हम एन्यूमरेशन डेटा के साथ कैसे काम करते हैं। सामान्यीकरण नहीं हुआ। आप आवश्यक तरीकों के एक सेट के साथ एक अलग वर्ग से मिलेंगे, फिर आपको दो कक्षाएं मिलेंगी, उसी के लिए, या यहां तक ​​कि कुछ रहस्यमय और जादुई। और दोष देने वाला कोई नहीं है। विरासत से छुटकारा पाने के लिए हमने जो गति हासिल की वह बहुत शानदार थी।

सहकर्मियों के बीच स्थानांतरण का विषय उठाकर, मुझे समर्थन मिला। यह पता चला कि न केवल मैं उनके साथ काम करने के लिए एकीकृत दृष्टिकोण की कमी से संतुष्ट नहीं था। उस समय, यह मुझे लग रहा था कि कोडिंग के कुछ घंटों में मैं वांछित परिणाम प्राप्त करूंगा और स्थिति को सही करने के लिए स्वेच्छा से। लेकिन तब मैं कितना गलत था ...

//    ,        . import {Enum} from "ts-jenum"; @Enum("text") export class State { static readonly NEW = new State("New"); static readonly ACTIVE = new State("Active"); static readonly BLOCKED = new State("Blocked"); private constructor(public text: string) { super(); } } //   console.log("" + State.ACTIVE); // Active console.log("" + State.BLOCKED); // Blocked console.log(State.values()); // [State.NEW, State.ACTIVE, State.BLOCKED] console.log(State.valueOf("New")); // State.NEW console.log(State.valueByName("NEW")); // State.NEW console.log(State.ACTIVE.enumName); // ACTIVE 

1. डेकोरेटर


कहाँ से शुरू करें? केवल एक ही बात दिमाग में आई: एक आधार के रूप में एक जावा जैसी गणना करें। लेकिन जब से मैं अपने सहयोगियों को दिखाना चाहता था, मैंने शास्त्रीय विरासत को छोड़ने का फैसला किया। इसकी जगह डेकोरेटर का इस्तेमाल करें। इसके अलावा, डेकोरेटर को तर्कों के साथ लागू किया जा सकता है ताकि एनुमरेशन को आसानी और स्वाभाविक रूप से आवश्यक कार्यक्षमता प्रदान की जा सके। कोडिंग में अधिक समय नहीं लगा, और कुछ घंटों के बाद मेरे पास पहले से ही ऐसा ही कुछ था:

डेकोरेटर
 export function Enum(idProperty?: string) { // tslint:disable-next-line return function <T extends Function, V>(target: T): T { if ((target as any).__enumMap__ || (target as any).__enumValues__) { const enumName = (target as any).prototype.constructor.name; throw new Error(`The enumeration ${enumName} has already initialized`); } const enumMap: any = {}; const enumMapByName: any = {}; const enumValues = []; // Lookup static fields for (const key of Object.keys(target)) { const value: any = (target as any)[key]; // Check static field: to be instance of enum type if (value instanceof target) { let id; if (idProperty) { id = (value as any)[idProperty]; if (typeof id !== "string" && typeof id !== "number") { const enumName = (target as any).prototype.constructor.name; throw new Error(`The value of the ${idProperty} property in the enumeration element ${enumName}. ${key} is not a string or a number: ${id}`); } } else { id = key; } if (enumMap[id]) { const enumName = (target as any).prototype.constructor.name; throw new Error(`An element with the identifier ${id}: ${enumName}.${enumMap[id].enumName} already exists in the enumeration ${enumName}`); } enumMap[id] = value; enumMapByName[key] = value; enumValues.push(value); Object.defineProperty(value, "__enumName__", {value: key}); Object.freeze(value); } } Object.freeze(enumMap); Object.freeze(enumValues); Object.defineProperty(target, "__enumMap__", {value: enumMap}); Object.defineProperty(target, "__enumMapByName__", {value: enumMapByName}); Object.defineProperty(target, "__enumValues__", {value: enumValues}); if (idProperty) { Object.defineProperty(target, "__idPropertyName__", {value: idProperty}); } //  values(), valueOf     ,    -. Object.freeze(target); return target; }; } 

और यहाँ मुझे पहली असफलता का सामना करना पड़ा। यह पता चला कि आप डेकोरेटर की मदद से टाइप नहीं बदल सकते। इस विषय पर Microsoft की भी अपील है: क्लास डेकोरेटर म्यूटेशन । जब मैं कहता हूं कि आप प्रकार नहीं बदल सकते हैं, तो मेरा मतलब है कि आपके आईडीई को इस बारे में कुछ भी पता नहीं होगा और कोई संकेत और पर्याप्त ऑटो-पूर्ति की पेशकश नहीं करेगा। और आप जितना चाहें उतना प्रकार बदल सकते हैं, केवल इसका अच्छा ...

2. वंशानुक्रम


जैसा कि मैंने खुद को मनाने की कोशिश नहीं की, लेकिन मुझे सामान्य वर्ग के आधार पर स्थानान्तरण बनाने के विचार पर लौटना पड़ा। हां, और इसमें गलत क्या है? मुझे खुद पर गुस्सा आ रहा था। समय समाप्त हो रहा है, समूह के लोग, भगवान न करें, मैं सज्जाकारों पर समय बिता रहा हूं। एक घंटे में सामान्य रूप से फाइल करना और आगे बढ़ना संभव था। तो ऐसा हो। तुरंत बेस क्लास के कोड को एन्युमरेबल और उच्छ्वासित किया, राहत महसूस की। मैंने ड्राफ्ट को सामान्य रिपॉजिटरी में फेंक दिया और अपने सहयोगी से समाधान की जांच करने के लिए कहा।

गणनीय
 // :     ,  -     export class Enumerable<T> { constructor() { const clazz = this.constructor as any as EnumStore; if (clazz.__enumMap__ || clazz.__enumValues__ || clazz.__enumMapByName__) { throw new Error(`It is forbidden to create ${clazz.name} enumeration elements outside the enumeration`); } } static values<T>(): ReadonlyArray<T> { const clazz = this as any as EnumStore; if (!clazz.__enumValues__) { throw new Error(`${clazz.name} enumeration has not been initialized. It is necessary to add the decorator @Enum to the class`); } return clazz.__enumValues__; } static valueOf<T>(id: string | number): T { const clazz = this as any as EnumStore; if (!clazz.__enumMap__) { throw new Error(`${clazz.name} enumeration has not been initialized. It is necessary to add the decorator @Enum to the class`); } const value = clazz.__enumMap__[id]; if (!value) { throw new Error(`The element with ${id} identifier does not exist in the $ {clazz.name} enumeration`); } return value; } static valueByName<T>(name: string): T { const clazz = this as any as EnumStore; if (!clazz.__enumMapByName__) { throw new Error(`${clazz.name} enumeration has not been initialized. It is necessary to add the decorator @Enum to the class`); } const value = clazz.__enumMapByName__[name]; if (!value) { throw new Error(`The element with ${name} name does not exist in the ${clazz.name} enumeration`); } return value; } get enumName(): string { return (this as any).__enumName__; } toString(): string { const clazz = this.constructor as any as EnumStore; if (clazz.__idPropertyName__) { const self = this as any; return self[clazz.__idPropertyName__]; } return this.enumName; } } 

लेकिन ट्रेजिकोमेडी पूरी तरह से भाप प्राप्त कर रहा था। टाइपस्क्रिप्ट संस्करण 2.6.2 मेरी मशीन पर स्थापित किया गया था, वह संस्करण जिसमें एक अमूल्य बग था। अनमोल, क्योंकि यह एक बग नहीं है, लेकिन एक योग्य है। बगल के कमरे से आवाज आई कि वह कुछ करने वाला नहीं है। त्रुटि संकलन ( ट्रांसप्लिंग )। मैं अपने खुद के कानों पर विश्वास नहीं कर सकता था, क्योंकि मैं हमेशा एक धक्का से पहले एक परियोजना को एक साथ रखता था, भले ही वह एक मसौदा था। और भीतर का स्वर फुसफुसाया: यह फियास्को है, भाई।

एक छोटे परीक्षण के बाद, मुझे एहसास हुआ कि यह एक टाइपस्क्रिप्ट संस्करण था। यह पता चला है कि यदि क्लास का जेनेरिक नाम स्थिर विधि में निर्दिष्ट जेनेरिक के नाम के साथ मेल खाता है, तो कंपाइलर ने इसे एक प्रकार माना। लेकिन जो कुछ भी था, अब यह टाइपस्क्रिप्ट के ज्ञान के लिए पहले से ही उस युद्ध के इतिहास का हिस्सा है।

लब्बोलुआब यह है कि के रूप में स्थानान्तरण के साथ समस्या थी और बनी रही। मेरी व्यथा ...

नोट: मैं २.६.२ के साथ अब स्वयं इस व्यवहार को पुन : पेश नहीं कर सकता, हो सकता है कि मैंने संस्करण के साथ कोई गलती की हो या परीक्षण के मामलों में कुछ न जोड़ा हो। और उपरोक्त समस्या के लिए अनुरोध स्थैतिक सदस्यों को वर्ग प्रकार के मापदंडों को संदर्भित करने की अनुमति दें

3. कास्टिंग समारोह


इस तथ्य के बावजूद कि स्थैतिक तरीकों में गणन वर्ग के प्रकार के स्पष्ट संकेत के साथ एक कुटिल समाधान था, उदाहरण के लिए, State.valueOf <State> (), यह किसी को भी पसंद नहीं आया, और सबसे पहले, मुझे। थोड़ी देर के लिए, मैंने कमबख्त स्थानांतरण को भी अलग रखा और विश्वास खो दिया कि मैं आमतौर पर इस समस्या को हल कर सकता हूं।

अपनी नैतिक ताकत हासिल करने के बाद, मैंने टाइपस्क्रिप्ट ट्रिक्स के लिए इंटरनेट की खोज की, जो देखा, जो पीड़ित था, भाषा के प्रलेखन को फिर से पढ़ें, बस मामले में, और फैसला किया, ताकि इसे खत्म करने के लिए, नौकरी खत्म करने के लिए। सात घंटे के निरंतर प्रयोगों ने, कॉफी के लिए भी कुछ नहीं ढूंढा, अपना परिणाम दिया। बस एक फ़ंक्शन, कोड की एक पंक्ति से मिलकर, सब कुछ अपनी जगह पर रखता है।

 export function EnumType<T>(): IStaticEnum<T> { return (<IStaticEnum<T>> Enumerable); } //  IStaticEnum : export interface IStaticEnum<T> { new(): {enumName: string}; values(): ReadonlyArray<T>; valueOf(id: string | number): T; valueByName(name: string): T; } 

और जावा जैसी गणना की घोषणा अब इस तरह दिखती है:

 import {Enum, EnumType, IStaticEnum} from "ts-jenum"; @Enum("text") export class State extends EnumType<State>() { static readonly NEW = new State("New"); static readonly ACTIVE = new State("Active"); static readonly BLOCKED = new State("Blocked"); private constructor(public text: string) { super(); } } //   console.log("" + State.ACTIVE); // Active console.log("" + State.BLOCKED); // Blocked console.log(State.values()); // [State.NEW, State.ACTIVE, State.BLOCKED] console.log(State.valueOf("New")); // State.NEW console.log(State.valueByName("NEW")); // State.NEW console.log(State.ACTIVE.enumName); // ACTIVE 

एक जिज्ञासा के बिना, IStaticEnum के एक अतिरिक्त आयात के साथ, जिसका उपयोग कहीं भी नहीं किया गया है (ऊपर उदाहरण देखें)। टाइपस्क्रिप्ट 2.6.2 के बहुत ही अशुभ संस्करण में, आपको इसे स्पष्ट रूप से निर्दिष्ट करने की आवश्यकता है। यहाँ विषय पर एक बग।

कुल मिलाकर


यदि आप लंबे समय तक पीड़ित हैं, तो कुछ काम करेगा। यहां किए गए कार्य के परिणाम के साथ जीथब को लिंक करें । खुद के लिए, मुझे पता चला कि टाइपस्क्रिप्ट एक ऐसी भाषा है, जिसमें शानदार फीचर हैं। इन अवसरों के बहुत सारे हैं कि आप उनमें डूब सकते हैं। और जो डूबना नहीं चाहता, तैरना सीख जाता है। यदि आप स्थानान्तरण के विषय पर लौटते हैं, तो आप देख सकते हैं कि दूसरे उनके साथ कैसे काम करते हैं:


अपने काम के बारे में लिखें, मुझे लगता है कि समुदाय में दिलचस्पी होगी। आपके धैर्य और रुचि के लिए आप सभी का धन्यवाद।

Source: https://habr.com/ru/post/hi420041/


All Articles