टाइपस्क्रिप्ट - त्रुटि वर्ग का विस्तार


81

मैं "त्रुटि" के बजाय कंसोल में छपे अपने "CustomError" वर्ग के नाम के साथ कोई त्रुटि नहीं होने पर कस्टम त्रुटि को फेंकने की कोशिश कर रहा हूं:

class CustomError extends Error { 
    constructor(message: string) {
      super(`Lorem "${message}" ipsum dolor.`);
      this.name = 'CustomError';
    }
}
throw new CustomError('foo'); 

आउटपुट है Uncaught Error: Lorem "foo" ipsum dolor।

मैं क्या उम्मीद: Uncaught CustomError: Lorem "foo" ipsum dolor।

मुझे आश्चर्य है कि अगर यह केवल टीएस का उपयोग करके किया जा सकता है (जेएस प्रोटोटाइप के साथ खिलवाड़ किए बिना)?

जवाबों:


78

क्या आप टाइपस्क्रिप्ट संस्करण 2.1 का उपयोग कर रहे हैं, और ES5 को ट्रांसप्लिंग कर रहे हैं? संभावित मुद्दों और वर्कअराउंड के लिए परिवर्तन परिवर्तन पृष्ठ के इस भाग की जाँच करें: https://github.com/Microsoft/TypeScript-wiki/blob/master/Breaking-Changes.md#extending-built-ins-like-error-array- और नक्शा- may-no-now-work

प्रासंगिक बिट:

सिफारिश के रूप में, आप किसी भी सुपर (...) कॉल के तुरंत बाद मैन्युअल रूप से प्रोटोटाइप को समायोजित कर सकते हैं।

class FooError extends Error {
    constructor(m: string) {
        super(m);

        // Set the prototype explicitly.
        Object.setPrototypeOf(this, FooError.prototype);
    }

    sayHello() {
        return "hello " + this.message;
    }
}

हालाँकि, FooError के किसी भी उपवर्ग को प्रोटोटाइप को मैन्युअल रूप से सेट करना होगा। Runtimes के लिए जो Object.setPrototypOf का समर्थन नहीं करते हैं, आप इसके बजाय उपयोग करने में सक्षम हो सकते हैं __proto__।

दुर्भाग्य से, ये वर्कअराउंड इंटरनेट एक्सप्लोरर 10 और पूर्व पर काम नहीं करेंगे। कोई स्वयं ही उदाहरण पर प्रोटोटाइप से विधियाँ कॉपी कर सकता है (यानी इस पर FooError.prototype), लेकिन प्रोटोटाइप श्रृंखला स्वयं तय नहीं की जा सकती।


2
हां, मैं ES5 के लिए ट्रांसप्लान कर रहा हूं। जैसा कि आपने सुझाव दिया मैंने प्रोटोटाइप सेट करने की कोशिश की, लेकिन दुर्भाग्य से, यह काम नहीं किया।
— डार्कसुलोंग

3
यह ध्यान देने योग्य है कि दुर्भाग्य से, यहां काम नहींObject.setPrototypeOf(this, this.constructor.prototype) करेगा , वर्ग को स्पष्ट रूप से संदर्भित करना होगा।
— जॉन वीज

1
@ जोहानिसिज यदि आप जिस फिक्स का उल्लेख कर रहे हैं, वह काम करता है, तो पहली जगह में फिक्स होने के लिए कुछ भी नहीं होगा, यदि आप this.constructorइस बिंदु से प्रोटोटाइप पढ़ सकते हैं , तो प्रोटोटाइप चेन पहले से ही बरकरार रहेगा।
— thetrompf

3
टाइपस्क्रिप्ट 2.2 के रूप में कंस्ट्रक्टर में सटीक प्रकार को हार्डकोड किए बिना इसे करने का एक तरीका प्रतीत होता है: Object.setPrototypeOf(this, new.target.prototype); टाइपस्क्रिप्ट
— lang.org/docs/handbook/release-notes/…

65

समस्या यह है कि जावास्क्रिप्ट निर्मित बिल्ट-इन क्लास Error, निर्माण की जाने वाली वस्तु को स्विच करके प्रोटोटाइप श्रृंखला को तोड़ती है (यानी this) जब आप कॉल करते हैं तो एक नई, अलग वस्तु, superऔर उस नई वस्तु में अपेक्षित प्रोटोटाइप श्रृंखला नहीं होती है, अर्थात यह एक उदाहरण है। की Errorकी नहीं CustomError।

इस समस्या को 'new.target' का उपयोग करके सुरुचिपूर्ण ढंग से हल किया जा सकता है, जो कि टाइपस्क्रिप्ट 2.2 के बाद से समर्थित है, यहां देखें: https://www.typescriptlang.org/docs/handbook/release-notes/typescript-2-2.html

class CustomError extends Error {
  constructor(message?: string) {
    // 'Error' breaks prototype chain here
    super(message); 

    // restore prototype chain   
    const actualProto = new.target.prototype;

    if (Object.setPrototypeOf) { Object.setPrototypeOf(this, actualProto); } 
    else { this.__proto__ = actualProto; } 
  }
}

उपयोग करने new.targetका यह फायदा है कि आपको प्रोटोटाइप को हार्डकोड करने की आवश्यकता नहीं है, जैसे कि यहां कुछ अन्य उत्तर प्रस्तावित हैं। इससे फिर से यह फायदा होता है कि इनसे विरासत में मिली कक्षाएं CustomErrorअपने आप सही प्रोटोटाइप श्रृंखला भी प्राप्त कर लेंगी।

यदि आप प्रोटोटाइप (उदाहरण के लिए Object.setPrototype(this, CustomError.prototype)) को हार्डकोड करते हैं , तो CustomErrorस्वयं एक काम करने वाली प्रोटोटाइप श्रृंखला होगी, लेकिन इससे प्राप्त होने वाली कोई भी कक्षाएं CustomErrorटूट जाएंगी, उदाहरण के लिए अपेक्षा के अनुरूप class VeryCustomError < CustomErrorनहीं होगा instanceof VeryCustomError, लेकिन केवल instanceof CustomError।

इसे भी देखें: https://github.com/Microsoft/TypeScript/issues/13965#issuecomment-27858500200


this.__proto__निजी या सार्वजनिक होना चाहिए?
— lonix

आप इसे निजी तौर पर सोच सकते हैं, क्योंकि आप इसे कंस्ट्रक्टर के बाहर एक्सेस करने की कोशिश नहीं करते हैं। इसलिए जब तक आप केवल टाइपस्क्रिप्ट में इसका उपयोग करते हैं जो ES5 के लिए संकलित हो जाता है, तो यह कोई समस्या नहीं होनी चाहिए, क्योंकि यह टाइपस्क्रिप्ट सुविधा केवल संकलन-समय की सुविधा है। फ़ील्ड हमेशा आपके संकलित कोड में सार्वजनिक रूप से सुलभ होगी। वर्तमान में वास्तविक "निजी वर्ग के क्षेत्र" का प्रस्ताव केवल प्रायोगिक चरण में है। और __proto__संपत्ति को भी पदावनत किया जाता है और केवल कुछ हद तक पीछे की ओर संगत होने के लिए मानकीकृत किया जाता है। दोनों पर अधिक जानकारी के लिए MDN देखें ।
— ट्रवे

1
या आप बस कुछ ऐसा कर सकते हैं (this as any).__proto__ = actualProto;। यह जो मैंने किया है। हैकी, लेकिन इसलिए मुझे किसी भी वर्ग की घोषणाओं को बदलने की आवश्यकता नहीं है।
— Trve

18

यह ES2015 ( https://jsfiddle.net/x40n2gyr/ ) में सही तरीके से काम करता है । सबसे अधिक संभावना है, समस्या यह है कि टाइपस्क्रिप्ट कंपाइलर ES5 को ट्रांसप्लिंग कर रहा है, और Errorकेवल 5 जी सुविधाओं का उपयोग करके सही ढंग से उप-वर्ग नहीं किया जा सकता है; यह केवल ES2015 और इसके बाद के संस्करण ( classया अधिक अस्पष्ट, Reflect.construct) का उपयोग करके सही ढंग से उप-वर्गित किया जा सकता है । ऐसा इसलिए है क्योंकि जब आप Errorएक फ़ंक्शन ( newES2015, superया Reflect.construct) के बजाय ( या के बजाय ) कहते हैं, तो यह अनदेखा करता है thisऔर एक नया बनाता है Error।

शायद आपको अपूर्ण आउटपुट के साथ रहना होगा जब तक आप ES2015 या उच्चतर लक्ष्य नहीं कर सकते ...


2
यह। यह। यह और फिर यह।
— लुका टी

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

10

टाइपस्क्रिप्ट 2.2 के माध्यम से यह किया जा सकता है new.target.prototype। https://www.typescriptlang.org/docs/handbook/release-notes/typescript-2-2.html##ample

class CustomError extends Error {
    constructor(message?: string) {
        super(message); // 'Error' breaks prototype chain here
        this.name = 'CustomError';
        Object.setPrototypeOf(this, new.target.prototype); // restore prototype chain
    }
}

1
यह
— .name

6

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

function CustomError(message) {
  this.name = 'CustomError';
  this.message = message || 'Default Message';
  this.stack = (new Error()).stack;
}
CustomError.prototype = Object.create(Error.prototype);
CustomError.prototype.constructor = CustomError;

throw new CustomError('foo');

यह एसओ कोड स्निपेट में काम नहीं करता है, लेकिन यह क्रोम कंसोल और मेरे टाइपस्क्रिप्ट प्रोजेक्ट में करता है:

यहाँ छवि विवरण दर्ज करें


1
विचार पसंद आया, इसे टाइपस्क्रिप्ट में लागू करने में विफल रहा
— जूलियस failed।

वास्तव में? इसने मेरे टाइपस्क्रिप्ट प्रतिक्रियात्मक प्रोजेक्ट पर काम किया।
— Mμ।

खैर, मैं भी बॉयलरप्लेट भाग को अमूर्त करना चाहता था और शायद ऐसा करने में विफल रहा, मैं फिर से किसी अन्य शिननिगेंस के बिना कोशिश करूँगा।
— जूलियस Š।

अच्छा विचार है, लेकिन यह इस संदेश के साथ विफल रहता है जब strict: truetsconfig में: stackoverflow.com/questions/43623461/…
— André

यह काम करता है, लेकिन ब्राउज़र कंसोल में त्रुटि स्वरूपण को तोड़ता है। Chrome कंसोल में आपके CustomError इंस्टेंस को सामान्य अच्छे फॉर्मेट के साथ प्रदर्शित नहीं किया जाता है जो "सही" त्रुटियां प्राप्त करता है, लेकिन "जेनेरिक ऑब्जेक्ट" प्रारूप में, जिसे पढ़ना कठिन है।
— क्रिस्टियन हैनकैंप

2

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

class MainErrorClass extends Error {
  constructor() {
    super()
    Object.setPrototypeOf(this, new.target.prototype)
  }
}

class SomeNewError extends MainErrorClass {} 

...

का उपयोग करना new.target.prototypeहर एक के कंस्ट्रक्टर को अपडेट करने की आवश्यकता के बिना इनहेरिट करने वाले त्रुटि वर्गों के सभी प्राप्त करने की कुंजी थी।

बस यही उम्मीद करना भविष्य में किसी और को सिरदर्द देता है!


1

इसे इस्तेमाल करे...

class CustomError extends Error { 

  constructor(message: string) {
    super(`Lorem "${message}" ipsum dolor.`)
  }

  get name() { return this.constructor.name }

}

throw new CustomError('foo')

लेकिन यह पहले से मौजूद कार्यक्षमता का उपयोग नहीं करता है और हमें बोलने के लिए पहिया को फिर से लागू करने की आवश्यकता है। या मैं गलत हूँ?
— जोशुआट्री

@JoshuaTree क्या पहले से मौजूद कार्यक्षमता समाधान का उपयोग नहीं करती है? कौन सा पहिया फिर से लागू किया जा रहा है? आप गलत नहीं हो सकते हैं लेकिन आपके तर्क थोड़े और विशिष्ट हो सकते हैं।
— flcoder

माफ कीजिएगा यह मेरी गलती है। मैंने टाइपस्क्रिप्ट src की जाँच की और मुझे लगता है कि यह एक इंटरफ़ेस है, गेटर्स और सेटर के साथ वास्तविक वस्तु नहीं। आपका कार्यान्वयन किसी भी मौजूदा कार्यक्षमता को ओवरराइड नहीं करता है।
— जोशुआट्री
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.