Typescript: دعم تجاوز الكلمات الأساسية في أساليب الفصل

تم إنشاؤها على ١٠ فبراير ٢٠١٥  ·  210تعليقات  ·  مصدر: microsoft/TypeScript

(التحديث عن طريقRyanCavanuagh)

يرجى الاطلاع على هذا التعليق قبل طلب "أي تحديثات" ، "الرجاء إضافة هذا الآن" ، وما إلى ذلك. ستتم إزالة التعليقات التي لا تضيف بشكل هادف إلى المناقشة للحفاظ على طول الموضوع معقولًا إلى حد ما.


(ملاحظة ، هذا _ ليس_ نسخة مكررة من الإصدار رقم 1524. الاقتراح هنا يتماشى بشكل أكبر مع سطور محدد التجاوز C ++ ، مما يجعله أكثر منطقية للنص المطبوع)

قد تكون كلمة رئيسية للتجاوز مفيدة للغاية في الكتابة المطبوعة. قد تكون كلمة رئيسية اختيارية في أي طريقة تتجاوز أسلوب الفئة الممتازة ، وتشبه محدد التجاوز في C ++ يشير إلى نية "_يجب أن يتطابق اسم + توقيع هذه الطريقة دائمًا مع الاسم + توقيع أسلوب الفئة الممتازة_" . يؤدي هذا إلى اكتشاف مجموعة كاملة من المشكلات في قواعد التعليمات البرمجية الأكبر التي يمكن تفويتها بسهولة.

مرة أخرى مشابهة لـ C ++ ، _it ليس خطأ في حذف الكلمة الأساسية للتجاوز من طريقة تم تجاوزها_. في هذه الحالة ، يعمل المترجم تمامًا كما يفعل حاليًا ، ويتخطى عمليات التحقق من وقت الترجمة الإضافية المرتبطة بالكلمة الأساسية للتجاوز. يسمح هذا بسيناريوهات جافا سكريبت غير النمطية الأكثر تعقيدًا حيث لا يتطابق تجاوز الفئة المشتقة تمامًا مع توقيع الفئة الأساسية.

استخدام المثال

class Animal {
    move(meters:number):void {
    }
}

class Snake extends Animal {
    override move(meters:number):void {
    }
}

مثال على ترجمة شروط الخطأ

// Add an additional param to move, unaware that the intent was 
// to override a specific signature in the base class
class Snake extends Animal {
    override move(meters:number, height:number):void {
    }
}
// COMPILE ERROR: Snake super does not define move(meters:number,height:number):void

// Rename the function in the base class, unaware that a derived class
// existed that was overriding the same method and hence it needs renaming as well
class Animal {
    megamove(meters:number):void {
    }
}
// COMPILE ERROR: Snake super does not define move(meters:number):void

// Require the function to now return a bool, unaware that a derived class
// existed that was still using void, and hence it needs updating
class Animal {
    move(meters:number):bool {
    }
}
// COMPILE ERROR: Snake super does not define move(meters:number):void

التحسس الذكي

بالإضافة إلى التحقق الإضافي من وقت الترجمة ، توفر الكلمة الأساسية للتجاوز آلية لتحسس الكتابة المطبوعة لعرض الطرق الفائقة المتاحة وتحديدها بسهولة ، حيث يكون القصد من تجاوز أحدها على وجه التحديد في فئة مشتقة. في الوقت الحالي ، يعد هذا الأمر صعبًا للغاية ويتضمن تصفح سلسلة الفئة الفائقة ، والعثور على الطريقة التي تريد تجاوزها ، ثم نسخها ولصقها في الفئة المشتقة لضمان تطابق التوقيعات.

آلية التحسس الذكي المقترحة

ضمن التصريح الطبقي:

  1. اكتب تجاوز
  2. تظهر قائمة منسدلة للإكمال التلقائي لجميع الطرق الممتازة للفصل
  3. يتم تحديد طريقة في القائمة المنسدلة
  4. ينبعث توقيع الأسلوب في إعلان الفئة بعد الكلمة الأساسية للتجاوز.
Add a Flag Revisit Suggestion

التعليق الأكثر فائدة

يرجى المعذرة عن الأنين ، ولكن بصراحة ، بينما تنطبق حجتك على الكلمة الرئيسية public بلغة يكون فيها كل شيء عامًا بشكل افتراضي ، حيث يقسم العالم تمامًا بالفعل ، مع وجود أشياء مثل abstract و override اختياري ستساعد الكلمة الرئيسية

التجاوز هو أحد الجوانب القليلة المتبقية شديدة الحساسية من الأخطاء المطبعية للغة ، لأن اسم طريقة التجاوز الخاطئ ليس مشكلة وقت ترجمة واضحة. فائدة override واضحة ، لأنها تسمح لك بتحديد نيتك في التجاوز - إذا لم تكن الطريقة الأساسية موجودة ، فسيكون خطأ وقت التجميع. كل نحيي نظام الكتابة. لماذا لا يريد أي شخص هذا؟

ال 210 كومينتر

حقا اقتراح جيد.

ومع ذلك ، ماذا عن الأمثلة التالية ، والتي تعتبر صالحة تلغي لي:

class Snake extends Animal {
    override move(meters:number, height=-1):void {
    }
}
class A {...}
class Animal {
    setA(a: A): void {...}
    getA(): A {...}
}

class B extends A {...}
class Snake extends Animal {
    override setA(a: B): void {...}
    override getA(): B {...}
}

بالإضافة إلى ذلك ، أود إضافة علامة مترجم لفرض وجود كلمة رئيسية للتجاوز (أو الإبلاغ عنها كتحذير).
والسبب هو الالتقاط عند إعادة تسمية طريقة في فئة أساسية أن الفئات الموروثة تنفذها بالفعل (ولكن ليس من المفترض أن تكون تجاوزًا).

آه أمثلة لطيفة. بشكل عام ، أتوقع استخدام الكلمة الأساسية للتجاوز لفرض المطابقة التامة للتوقيعات ، لأن الهدف من استخدامها هو الحفاظ على تسلسل هرمي صارم للفئة المكتوبة. لذلك لمعالجة الأمثلة الخاصة بك:

  1. إضافة معلمة افتراضية إضافية. سيؤدي هذا إلى حدوث خطأ في التحويل البرمجي: لا يقوم Snake super بتعريف الحركة (عداد: رقم): باطل. بينما الطريقة المشتقة متسقة وظيفيًا ، قد لا يتوقع رمز العميل الذي يستدعي Animal.move أن تكون الفئات المشتقة أيضًا عاملًا في الارتفاع (نظرًا لأن واجهة برمجة التطبيقات الأساسية لا تعرضها).
  2. سيؤدي هذا (ويجب أن يؤدي دائمًا) إلى حدوث خطأ في التحويل البرمجي ، لأنه غير متسق وظيفيًا. ضع في اعتبارك الإضافة التالية للمثال:
class C extends A {...}
var animal : Animal = new Snake();
animal.setA(new C());
// This will have undefined run-time behavior, as C will be interpreted as type B in Snake.setA

لذا فإن المثال (2.) هو في الواقع عرض توضيحي رائع لكيفية تمكن كلمة رئيسية تجاوز من التقاط حالات الحافة الدقيقة في وقت التجميع الذي قد يتم تفويته بخلاف ذلك! :)

وأود أن أؤكد مرة أخرى أن كلا المثالين قد يكونان صالحين في سيناريوهات جافا سكريبت محددة / متقدمة قد تكون مطلوبة ... في هذه الحالة يمكن للمستخدمين فقط اختيار حذف الكلمة الأساسية للتجاوز.

سيكون هذا مفيدا. نحن نعمل حاليًا على حل هذا الأمر من خلال تضمين مرجع وهمي للطريقة الفائقة:

class Snake extends Animal {
    move(meters:number, height?:number):void {
         super.move; // override fix
    }
}

لكن هذا فقط يحمي من الحالة الثانية: إعادة تسمية الأساليب الفائقة. لا تؤدي التغييرات في التوقيع إلى حدوث خطأ في التحويل البرمجي. علاوة على ذلك ، من الواضح أن هذا اختراق.

لا أعتقد أيضًا أن المعلمات الافتراضية والاختيارية في توقيع طريقة الفئة المشتقة يجب أن تؤدي إلى حدوث خطأ في التجميع. قد يكون هذا صحيحًا ولكنه يتعارض مع المرونة الكامنة في JavaScript.

تضمين التغريدة
يبدو أننا لا نتوقع نفس السلوك.
يمكنك استخدام هذه الكلمة الأساسية للتجاوز لضمان نفس التوقيع ، بينما سأستخدمها أكثر كخيار جاهز (لذلك طلبي لإضافة خيار مترجم لفرض استخدامه).
في الحقيقة ما أتوقعه حقًا هو أن TS يكتشف طرق تجاوز غير صالحة (حتى بدون استخدام التجاوز).
عادة:

class Snake extends Animal {
    move(meters:number, height:number):void {}
}

يجب أن يثير خطأ ، لأنه حقًا تجاوز لـ Animal.move () (سلوك JS) ، لكنه غير متوافق (لأنه ليس من المفترض أن يكون الارتفاع اختياريًا ، في حين أنه سيكون غير معرف إذا تم استدعاؤه من "مرجع" حيوان).
في الواقع ، فإن استخدام التجاوز سيؤكد فقط (بواسطة المترجم) أن هذه الطريقة موجودة بالفعل في الفئة الأساسية (وبالتالي مع توقيع متوافق ، ولكن بسبب النقطة السابقة ، وليس بسبب الكلمة الأساسية للتجاوز).

stephanedr ، بصفتي مستخدمًا واحدًا ، أتفق معك حقًا في أن المترجم يجب أن يؤكد التوقيع دائمًا ، لأنني شخصياً أحب أن أفرض كتابة صارمة داخل التسلسلات الهرمية للفصل (حتى لو لم يكن جافا سكريبت!).

ومع ذلك ، باقتراح أن هذا السلوك اختياري عبر الكلمة الأساسية للتجاوز ، أحاول أن أضع في اعتبارك أن جافا سكريبت في النهاية غير مكتوب ، وبالتالي فإن فرض مطابقة التوقيع الصارمة افتراضيًا سيؤدي إلى عدم إمكانية التعبير عن بعض أنماط تصميم جافا سكريبت في تنضيد الكتابة.

rwyborn يسعدني أنك ذكرت تطبيق C ++ لأن هذا هو بالضبط ما تخيلته قبل أن أصل إلى هنا - اختياريًا. على الرغم من أن علامة المترجم التي فرضت استخدام الكلمة الأساسية للتجاوز ستظهر جيدًا في كتابي.

ستسمح الكلمة الأساسية بتجميع أخطاء الوقت للمطورين في الكتابة الخرقاء ، وهو أكثر ما يقلقني بشأن التجاوزات في شكلها الحالي.

class Base {
    protected commitState() : void {

    }
}


class Implementation extends Base {
    override protected comitState() : void {   /// error - 'comitState' doesn't exist on base type

    }
}

حاليًا (بدءًا من 1.4) ستعلن فئة Implementation أعلاه عن طريقة جديدة ولن يكون المطور أكثر حكمة حتى يلاحظوا أن الكود الخاص بهم لا يعمل.

نوقشت في مراجعة الاقتراح.

نحن بالتأكيد نفهم حالات الاستخدام هنا. المشكلة هي أن إضافتها في هذه المرحلة في اللغة تضيف مزيدًا من الالتباس أكثر مما تزيله. لا يعني الفصل الذي يحتوي على 5 طرق ، تم وضع علامة على 3 منها override ، على أن الطريقتين الأخريين _ لا _ يتخطيان. لتبرير وجوده ، سيحتاج المعدِّل حقًا إلى تقسيم العالم بشكل أكثر نظافة من ذلك.

يرجى المعذرة عن الأنين ، ولكن بصراحة ، بينما تنطبق حجتك على الكلمة الرئيسية public بلغة يكون فيها كل شيء عامًا بشكل افتراضي ، حيث يقسم العالم تمامًا بالفعل ، مع وجود أشياء مثل abstract و override اختياري ستساعد الكلمة الرئيسية

التجاوز هو أحد الجوانب القليلة المتبقية شديدة الحساسية من الأخطاء المطبعية للغة ، لأن اسم طريقة التجاوز الخاطئ ليس مشكلة وقت ترجمة واضحة. فائدة override واضحة ، لأنها تسمح لك بتحديد نيتك في التجاوز - إذا لم تكن الطريقة الأساسية موجودة ، فسيكون خطأ وقت التجميع. كل نحيي نظام الكتابة. لماذا لا يريد أي شخص هذا؟

أتفق بنسبة 100٪ مع hdachev ، فإن التناقض الصغير المشار إليه أيضًا بواسطة RyanCavanaugh يتم موازنته بسهولة بمزايا الكلمة الرئيسية في جلب عمليات التحقق من وقت التجميع إلى تجاوزات الطريقة. أود أن أشير مرة أخرى إلى أن C ++ تستخدم كلمة رئيسية اختيارية للتجاوز بنجاح بنفس الطريقة تمامًا كما هو مقترح للطباعة.

لا يمكنني التأكيد بشكل كافٍ على مقدار الفرق الذي يحدثه التحقق من تجاوز الاختلاف في قاعدة التعليمات البرمجية واسعة النطاق باستخدام أشجار OO المعقدة.

أخيرًا ، أود أن أضيف أنه إذا كان عدم الاتساق في كلمة رئيسية اختيارية يمثل مصدر قلق حقًا ، فيمكن استخدام نهج C # ، وهذا هو الاستخدام الإلزامي للكلمات الرئيسية "الجديدة" أو "التجاوز":

class Dervied extends Base {

    new FuncA(newParam) {} // "new" says that I am implementing a new version of FuncA() with a different signature to the base class version

    override FuncB() {} // "override" says that I am implementing exactly the same signature as the base class version

    FuncC() {} // If FuncC exists in the base class then this is a compile error. I must either use the override keyword (I am matching the signature) or the new keyword (I am changing the signature)
}

هذا ليس مشابهًا لـ public لأنه من المعروف أن الخاصية التي لا تحتوي على معدِّل وصول عامة ؛ الطريقة التي لا تحتوي على override _not_ معروفة بأنها غير قابلة للتجاوز.

إليك حل تم التحقق من وقت التشغيل باستخدام أدوات الزخرفة (الواردة في TS1.5) والتي تنتج رسائل خطأ جيدة مع القليل جدًا من النفقات العامة:

/* Put this in a helper library somewhere */
function override(container, key, other1) {
    var baseType = Object.getPrototypeOf(container);
    if(typeof baseType[key] !== 'function') {
        throw new Error('Method ' + key + ' of ' + container.constructor.name + ' does not override any base class method');
    }
}

/* User code */
class Base {
    public baseMethod() {
        console.log('Base says hello');
    }
}

class Derived extends Base {
    // Works
    <strong i="9">@override</strong>
    public baseMethod() {
        console.log('Derived says hello');
    }

    // Causes exception
    <strong i="10">@override</strong>
    public notAnOverride() {
        console.log('hello world');
    }
}

ينتج عن تشغيل هذا الرمز خطأ:

خطأ: لا يتجاوز الأسلوب notAnOverride للمشتقة أي أسلوب للفئة الأساسية

نظرًا لأن هذا الرمز يعمل في وقت تهيئة الفصل ، فلن تحتاج حتى إلى اختبارات وحدة خاصة بالطرق المعنية ؛ سيحدث الخطأ بمجرد تحميل الكود الخاص بك. يمكنك أيضًا التبديل إلى إصدار "سريع" override لا يقوم بأي فحص لعمليات نشر الإنتاج.

RyanCavanaugh إذن نحن في Typescript 1.6 وما زال المصممون ميزة تجريبية ، وليست شيئًا أريد نشره في قاعدة بيانات إنتاجية واسعة النطاق كقرصنة لإلغاء العمل.

لتجربة زاوية أخرى ، فإن كل لغة مكتوبة شائعة تدعم الكلمة الأساسية "override" ؛ Swift و ActionScript و C # و C ++ و F # على سبيل المثال لا الحصر. تشترك كل هذه اللغات في المشكلات الثانوية التي عبرت عنها في هذا الموضوع حول التجاوز ، ولكن من الواضح أن هناك مجموعة كبيرة ترى أن فوائد التجاوز بعيدة المدى تزن هذه المشكلات الصغيرة.

هل تستند اعتراضاتك على التكلفة / المنفعة فقط؟ إذا كنت سأقوم بالفعل بتنفيذ هذا في العلاقات العامة ، فهل سيتم قبوله؟

إنها ليست مجرد قضية تكلفة / فائدة. كما أوضح ريان ، تكمن المشكلة في أن وضع علامة على طريقة ما على أنها تجاوز لا يعني أن طريقة أخرى _ ليست تجاوزًا. الطريقة الوحيدة التي سيكون من المنطقي أن يتم تمييزها بكلمة رئيسية override (والتي إذا طلبناها ، ستكون تغييرًا مفاجئًا).

DanielRosenwasser كما هو موضح أعلاه ، في C ++ ، تعد الكلمة الأساسية للتجاوز اختيارية (تمامًا كما هو مقترح لـ Typescript) ، ومع ذلك لا يستخدمها الجميع ، وهي مفيدة بشكل كبير في قواعد التعليمات البرمجية الكبيرة. علاوة على ذلك ، في Typescript ، من المنطقي جدًا أن تكون اختيارية بسبب التحميل الزائد لوظيفة جافا سكريبت.

class Base {
    method(param: number): void { }
}

class DerivedA extends Base {
    // I want to *exactly* implement method with the same signature
    override method(param: number): void {}
}

class DerivedB extends Base {
    // I want to implement method, but support an extended signature
    method(param: number, extraParam: any): void {}
}

أما بالنسبة للكل "لا يعني أن طريقة أخرى ليست حجة تجاوز" ، فهي مشابهة تمامًا للحجة "الخاصة". يمكنك كتابة قاعدة بيانات كاملة دون استخدام الكلمة الأساسية الخاصة. لن يتم الوصول إلى بعض المتغيرات في قاعدة الشفرة هذه إلا بشكل خاص وسيعمل كل شيء بشكل جيد. ومع ذلك ، فإن كلمة "private" هي بعض السكر النحوي الإضافي الذي يمكنك استخدامه لإخبار المترجم "لا حقًا ، خطأ في التجميع إذا حاول شخص ما الوصول إلى هذا". وبنفس الطريقة ، فإن "overload" هي عبارة عن سكر نحوي إضافي لإخبار المترجم "أريد أن يتطابق هذا تمامًا مع إعلان الفئة الأساسية. إذا لم يكن هناك خطأ في التحويل البرمجي".

أتعلمون ماذا ، أعتقد أنكم تركزون على التفسير الحرفي لـ "تجاوز". ما يتم ترميزه حقًا هو "أسلوب_المطابقة_المطابقة_من_الصف_السوبر" ، ولكن هذا ليس مقروءًا تمامًا :)

class DerivedA extends Base {
    exactly_match_signature_of_superclass_method method(param: number): void {}
}

أرغب أيضًا في إتاحة الكلمة الأساسية للتجاوز ، وجعل المترجم ينشئ خطأ إذا كانت الطريقة التي تم تمييزها للتجاوز غير موجودة ، أو لها توقيع مختلف ، في الفئة الأساسية. من شأنه أن يساعد في سهولة القراءة وإعادة بناء ديون

+1 ، ستتحسن الأدوات أيضًا. حالة الاستخدام الخاصة بي تستخدم رد الفعل. يجب أن أتحقق من التعريف في كل مرة أستخدم فيها طرق ComponentLifecycle :

ج #
واجهة دورة حياة المكون

{
componentWillMount؟ (): void؛
componentDidMount؟ (): باطل؛
componentWillReceiveProps؟ (nextProps: P، nextContext: any): void؛
shouldComponentUpdate؟ (nextProps: P، nextState: S، nextContext: أي): منطقية؛
المكونWillUpdate؟ (nextProps: P، nextState: S، nextContext: any): void؛
ComponDidUpdate؟ (prevProps: P، prevState: S، prevContext: any): void؛
componentWillUnmount؟ (): void؛
}

With override, or other equivalent solution,you'll get a nice auto-completion. 

One problem however is that I will need to override interface methods...

``` C#
export default class MyControlextends React.Component<{},[}> {
    override /*I want intellisense here*/ componentWillUpdate(nextProps, nextState, nextContext): void {

    }
}

olmobrutall يبدو أن حالة الاستخدام الخاصة بك يتم حلها بشكل أفضل من خلال خدمة اللغة التي تقدم إعادة بناء مثل "واجهة التنفيذ" أو تقديم إكمال أفضل ، وليس عن طريق إضافة كلمة رئيسية جديدة إلى اللغة.

دعنا لا نتشتت انتباهك :) ميزات خدمة اللغة ليست سوى جزء صغير من الحفاظ على التسلسلات الهرمية للواجهة في قاعدة رمز كبيرة. إلى حد بعيد ، فإن أكبر مكسب هو في الواقع الحصول على أخطاء وقت التجميع عندما لا يتوافق الفصل الدراسي في التسلسل الهرمي في مكان ما. هذا هو السبب في أن C ++ أضافت كلمة رئيسية اختيارية للتجاوز (تغيير غير منقطع). هذا هو السبب في أن كتابات التنصيب يجب أن تفعل الشيء نفسه.

وثائق Microsoft لتجاوز C ++ تلخص الأشياء بشكل جيد ، https://msdn.microsoft.com/en-us/library/jj678987.aspx

استخدم التجاوز للمساعدة في منع سلوك الوراثة غير المقصود في التعليمات البرمجية الخاصة بك. يوضح المثال التالي ، بدون استخدام تجاوز ، قد لا يكون سلوك وظيفة العضو للفئة المشتقة مقصودًا. المترجم لا يصدر أي أخطاء لهذا الكود.
...
عند استخدام تجاوز ، يقوم المترجم بإنشاء أخطاء بدلاً من إنشاء وظائف عضو جديدة بصمت.

إلى حد بعيد ، فإن أكبر مكسب هو في الواقع الحصول على أخطاء وقت التجميع عندما لا يتوافق الفصل الدراسي في التسلسل الهرمي في مكان ما.

علي ان اوافق. أحد المآزق التي تظهر في فرقنا هو أن الناس يعتقدون أنهم تجاوزوا الأساليب في حين أنهم في الواقع أخطأوا قليلاً في الكتابة أو قاموا بتوسيع الفصل الخطأ.

تعد تغييرات الواجهة في مكتبة أساسية عند العمل عبر العديد من المشاريع أصعب مما يجب أن تكون في لغة لها أجندة مثل TypeScript.

لدينا الكثير من الأشياء الرائعة ، ولكن هناك شذوذ مثل هذا ولا توجد خصائص ثابتة على مستوى الفصل.

RyanCavanaugh صحيح ، ولكن يمكن تشغيل خدمة اللغة بعد كتابة الإلغاء ، كما تفعل العديد من اللغات ، بدون الكلمة الرئيسية يكون من الصعب للغاية معرفة متى تكون اللحظة المناسبة.

حول واجهة التنفيذ ، لاحظ أن معظم الطرق الموجودة في الواجهة اختيارية ومن المفترض أن تتجاوز فقط القليل منها ، وليس الحزمة بأكملها. يمكنك فتح مربع حوار به مربعات اختيار ولكن لا يزال ...

وعلى الرغم من أن نقطة الألم الحالية هي العثور على اسم الطريقة ، فسيكون من الرائع في المستقبل أن يتم إعلامك بأخطاء وقت الترجمة إذا قام شخص ما بإعادة تسمية أو تغيير توقيع الطريقة الأساسية.

ألا يمكن حل التناقض فقط عن طريق إضافة تحذير عندما لا تكتب تجاوز؟ أعتقد أن الكتابة المطبوعة تقوم بالشيء الصحيح بإضافة تغييرات كسر صغيرة معقولة بدلاً من الحفاظ على القرارات السيئة لنهاية الوقت.

هناك أيضًا الملخصات الموجودة بالفعل ، وسوف يصنعون زوجين رائعين :)

شعرت بالحاجة إلى محدد "تجاوز" أيضًا. في المشاريع المتوسطة إلى الكبيرة ، تصبح هذه الميزة ضرورية ، ومع كل الاحترام الواجب ، آمل أن يعيد فريق Typescript النظر في قرار رفض هذا الاقتراح.

لأي شخص مهتم ، قمنا بكتابة قاعدة tslint مخصصة توفر نفس وظائف الكلمة الرئيسية للتجاوز باستخدام أداة تزيين ، على غرار اقتراح Ryan أعلاه ولكن تم التحقق منها في وقت الترجمة. سنقوم بفتح مصدر مفتوح قريبًا ، وسأعيد نشره عندما يكون متاحًا.

شعرت بشدة بضرورة وجود كلمة رئيسية "تجاوز" أيضًا.
في حالتي ، قمت بتغيير بعض اسم الطريقة في فئة أساسية ونسيت إعادة تسمية بعض أسماء طرق التجاوز. بالطبع ، هذا يؤدي إلى بعض الأخطاء.

ولكن ، إذا كانت هناك مثل هذه الميزة ، فيمكننا العثور على طرق الفصل هذه بسهولة.

كما ذكر RyanCavanaugh ، إذا كانت هذه الكلمة الأساسية مجرد كلمة رئيسية اختيارية ، فإن هذه الميزة تسبب الارتباك. إذن ، ما رأيك في عمل إشارة إلى شيء ما في tsc لتمكين هذه الميزة؟

يرجى إعادة النظر في هذه الميزة مرة أخرى ....

بالنسبة لي ، إذا كانت الكلمة الأساسية للتجاوز مفيدة ، فيجب أن يتم فرضها ، كما هو الحال في C #. إذا حددت طريقة في C # تتجاوز توقيع الطريقة الأساسية ، يجب أن تسميها إما تجاوز أو جديدة.

C ++ مزعجة وأقل شأناً مقارنة بـ C # في بعض الأماكن لأنها تجعل الكثير من الكلمات الرئيسية اختيارية ، وبالتالي تمنع _ التناسق_. على سبيل المثال ، إذا قمت بزيادة التحميل على طريقة افتراضية ، فيمكن تمييز الطريقة المحملة بشكل زائد على أنها افتراضية أم لا - وفي كلتا الحالتين ستكون افتراضية. أفضل هذا الأخير لأنه يساعد المطورين الآخرين الذين يقرؤون الكود ، لكن لا يمكنني الحصول على المترجم لفرض إدخاله ، مما يعني أن قاعدة الشفرة لدينا ستفتقد بلا شك إلى الكلمات الرئيسية الافتراضية حيث ينبغي أن تكون بالفعل. الكلمة الأساسية تجاوز اختيارية بالمثل. كلاهما حماقة في رأيي. ما يتم تجاهله هنا هو أن الكود يمكن أن يكون بمثابة توثيق وتحسين إمكانية الصيانة من خلال فرض الحاجة إلى الكلمات الرئيسية بدلاً من نهج "خذها أو اتركها". الكلمة الأساسية "رمي" في C ++ مشابهة.

لتحقيق الهدف أعلاه في TypeScript ، يحتاج المترجم إلى إشارة "لتمكين" هذا السلوك الصارم أم لا.

بقدر ما يتعلق الأمر بتوقيعات الوظيفة للقاعدة والتجاوز ، يجب أن تكون متطابقة. ولكن سيكون من المرغوب فيه السماح بالتباين المشترك في مواصفات نوع الإرجاع لفرض فحص وقت الترجمة.

أنا قادم إلى TS من AS3 ، لذا بالطبع ، سأقوم بإدلاء تصويتي هنا للحصول على كلمة رئيسية override أيضًا. بالنسبة للمطور الجديد في أي قاعدة بيانات معينة ، فإن رؤية override هو دليل كبير على ما يمكن أن يحدث في فصل (طفل). أعتقد أن مثل هذه الكلمة الرئيسية تضيف قيمة كبيرة. سأختار أن أجعله إلزاميًا ، لكن يمكنني أن أرى كيف سيكون تغييرًا جذريًا وبالتالي يجب أن يكون اختياريًا. لا أرى حقًا مشكلة في الغموض الذي يفرضه ، على الرغم من حساسي للفرق بين الكلمة الرئيسية الاختيارية override والافتراضية public .

لجميع مستخدمي +1 - هل يمكنك التحدث عما هو غير كافٍ بشأن حل الديكور الموضح أعلاه؟

بالنسبة لي ، يبدو الأمر وكأنه بناء مصطنع ، وليس خاصية للغة نفسها. وأعتقد أن هذا عن طريق التصميم ، لأن هذا ما هو عليه. الذي أعتقد أنه يرسل للمطور (حسنًا ، أنا على أي حال) رسالة مفادها أنه عابر ، وليس أفضل ممارسة.

من الواضح أن كل لغة لها نموذجها الخاص ، وكوني جديدًا على TypeScript فأنا بطيء في التحول النموذجي. يجب أن أقول على الرغم من أن override أشعر أنه أفضل ممارسة بالنسبة لي لعدد من الأسباب. أقوم بالتبديل إلى TypeScript لأنني ابتلعت تمامًا koolaid المكتوب بقوة ، وأعتقد أن التكلفة (من حيث ضغطات المفاتيح ومنحنى التعلم) تفوقها بشكل كبير الفوائد لكل من الشفرة الخالية من الأخطاء وفهم الكود. override جزء مهم جدًا من هذا اللغز ، ويقدم بعض المعلومات المهمة جدًا حول دور طريقة التجاوز.

بالنسبة لي ، لا يتعلق الأمر براحة IDE ، على الرغم من أن هذا رائع بلا شك عند دعمه بشكل صحيح ، وأكثر من ذلك حول الوفاء بما أعتقد أنه المبادئ التي بنيت عليها هذه اللغة.

RyanCavanaugh بعض المشاكل التي أراها:

  • سيكون بناء جملة المصمم أصعب على IDEs لتوفير إكمال الكود (نعم ، أعتقد أنه يمكن إجراؤه ولكنهم سيحتاجون إلى معرفة مسبقة)
  • يمكن للفئة المشتقة تغيير رؤية الطريقة - والتي لا ينبغي السماح بها بعبارات صارمة
  • يصعب على المصمم التحقق من قائمة الحجج وأنواعها ونوع الإرجاع المتوافق

بالرغم من ذلك ، يتحقق المترجم بالفعل من قائمة الوسائط ونوع الإرجاع. وأعتقد أنه حتى لو كان override موجودًا ككلمة رئيسية من الدرجة الأولى ، فلن نفرض بعض القواعد الجديدة حول مطابقة التوقيع

وأعتقد أنه حتى لو كان التجاوز موجودًا بالفعل ككلمة رئيسية من الدرجة الأولى ، فلن نفرض بعض القواعد الجديدة حول مطابقة التوقيع.

RyanCavanaugh إذن أعتقد أنك قد تكون على صفحة مختلفة حول الغرض من الكلمة الرئيسية. بيت القصيد من ذلك هو للحالات التي _ تريد_ لفرض مطابقة التوقيع. هذا خاص بنمط التصميم حيث يكون لديك تسلسل هرمي معقد عميق يجلس على فئة أساسية تحدد عقدًا من طرق الواجهة ، ويجب أن تتطابق جميع الفئات في التسلسل الهرمي تمامًا مع تلك التواقيع. من خلال إضافة الكلمة الأساسية للتجاوز على هذه الطرق في جميع الفئات المشتقة ، يتم تنبيهك إلى أي حالات يختلف فيها التوقيع عن العقد المحدد في الفئة الأساسية.

وأنا أقول إن هذه ليست حالة استخدام مقصورة على فئة معينة. عند العمل على قواعد التعليمات البرمجية الكبيرة (على سبيل المثال مئات أو آلاف الفئات) هذا يحدث _ كل يوم_ ، أي شخص يحتاج إلى تغيير توقيع الفئة الأساسية (تعديل العقد) ، وتريد من المترجم أن ينبهك إلى أي الحالات عبر التسلسل الهرمي التي لم تعد متطابقة.

سيحذر المترجم بالفعل من التجاوزات غير القانونية. المشكلة مع
التنفيذ الحالي هو عدم وجود نية عند إعلان تجاوز
طريقة.

يبدو أن المصمم المذكور من قبل يتعارض مع اللغة
تحاول تحقيقه. لا ينبغي أن يكون هناك تكبد تكلفة وقت التشغيل ل
شيء يمكن التعامل معه في وقت التجميع ، مهما كانت التكلفة صغيرة.
نريد اكتشاف أكبر قدر ممكن من الأخطاء التي تحدث دون الحاجة إلى ذلك
قم بتشغيل الكود لمعرفة ذلك.

من الممكن تحقيق ذلك باستخدام المصمم وتخصيص tslint مخصص
القاعدة ، ولكنها ستكون أفضل لنظام الأدوات والمجتمع من أجل
اللغة المستخدمة للحصول على كلمة رئيسية رسمية.

أعتقد أن كونك اختياريًا هو أحد الأساليب ، ولكن كما هو الحال مع بعض برامج التحويل البرمجي لـ C ++
هناك علامات يمكنك تعيينها والتي تفرض استخدامها (على سبيل المثال ، اقتراح تجاوز ،
تجاوز غير متناسق في عداد المفقودين). قد يبدو هذا أفضل نهج لتجنب
كسر التغييرات ويبدو متسقًا مع الميزات الجديدة الأخرى التي يتم إضافتها
في الآونة الأخيرة مثل أنواع الزينة والديكور.

يوم الأربعاء ، 23 آذار (مارس) 2016 الساعة 21:31 ، كتب روان ويبورن [email protected] :

وأعتقد أنه حتى لو كان التجاوز موجودًا ككلمة رئيسية من الدرجة الأولى ، فإننا
لن تفرض بعض القواعد الجديدة حول مطابقة التوقيع.

RyanCavanaugh https://github.com/RyanCavanaugh فأعتقد أنك قد تفعل ذلك
تكون في صفحة مختلفة حول الغرض من الكلمة الرئيسية. بيت القصيد من
إنه للحالات التي تريد فيها فرض مطابقة التوقيع. هذه
هو نمط التصميم حيث يكون لديك تسلسل هرمي عميق معقد
على فئة أساسية تحدد عقدًا من طرق الواجهة ، وجميع
يجب أن تتطابق الفئات في التسلسل الهرمي تمامًا مع تلك التوقيعات. بإضافة
تجاوز الكلمة الأساسية على هذه الطرق في جميع الفئات المشتقة ، أنت
نبه إلى أي حالات يكون فيها التوقيع مختلفًا عن العقد المنصوص عليه
في الفئة الأساسية.

وأنا أقول إن هذه ليست حالة استخدام مقصورة على فئة معينة. عند العمل على أ
قواعد الكود الكبيرة (مع مئات أو آلاف الفئات مثلاً) هذا هو _ كل
يوم_ حدوث ، أي شخص ما يحتاج إلى تغيير توقيع القاعدة
class (تعديل العقد) ، وتريد من المترجم أن ينبهك لأي
الحالات عبر التسلسل الهرمي التي لم تعد متطابقة.

-
أنت تتلقى هذا لأنك مشترك في هذا الموضوع.
قم بالرد على هذا البريد الإلكتروني مباشرة أو قم بعرضه على GitHub
https://github.com/Microsoft/TypeScript/issues/2000#issuecomment -200551774

kungfusheep fwiw ، يلتقط المترجم فئة معينة فقط من التجاوزات غير القانونية ، حيث يوجد تضارب مباشر بين المعلمات المعلنة. لا _لا _ يلتقط إضافة أو إزالة المعلمات ، ولا يلتقط تغييرات نوع الإرجاع. هذه الفحوصات الإضافية هي ما ستقوم بتشغيله أي كلمة رئيسية للتجاوز.

يحذرك المترجم بشكل صحيح إذا أضفت معلمة إلى دالة تجاوز.

ومع ذلك ، فإن إزالة المعلمة _تعتبر صالحة تمامًا_:

class BaseEventHandler {
  handleEvent(e: EventArgs, timestamp: number) { }
}

class DerivedEventHandler extends BaseEventHandler {
  handleEvent(e: EventArgs) {
    // I don't need timestamp, it's OK
  }
}

يعد تغيير نوع الإرجاع صالحًا تمامًا أيضًا:

class Base {
  specialClone(): Base { ... }
}
class Derived extends Base {
  specialClone(): Derived { ... }
}

RyanCavanaugh نعم ، إنها صالحة من منظور لغوي صارم ، لكن هذا هو السبب في أنني بدأت في استخدام مصطلح "عقد" أعلاه. إذا حددت فئة أساسية توقيعًا محددًا ، فعند استخدام التخطي ، أصرح بأنني أريد أن يتبع صفي المشتق هذا التوقيع بدقة. إذا أضافت الفئة الأساسية معلمات إضافية أو تغييرات في نوع الإرجاع ، فأنا أرغب في معرفة كل نقطة في قاعدة التعليمات البرمجية الخاصة بي لم تعد متطابقة ، لأن العقد الذي تمت كتابته في الأصل ضده قد تغير الآن بطريقة ما.

فكرة وجود تسلسل هرمي كبير للفئة لكل منها تبديلات مختلفة قليلاً للطرق الأساسية (حتى لو كانت صالحة من منظور لغوي) هي بمثابة كابوس وتعيدني إلى الأيام القديمة السيئة لجافا سكريبت قبل ظهور الكتابة المطبوعة :)

إذا كانت الاختيارية هي المشكلة الرئيسية في الكلمة الأساسية ، فلماذا لا تجعلها إلزامية عندما يتم تعريف طريقة الفئة الأساسية على أنها abstract ؟ بهذه الطريقة ، إذا كنت ترغب في فرض النمط بشكل صارم ، يمكنك ببساطة إضافة فئة أساسية مجردة. لتجنب كسر الكود ، يمكن لمحول المترجم تعطيل الفحص.

يبدو أننا نتحدث عن مجموعتين مختلفتين من التوقعات الآن. هناك حالة التجاوز المفقود / التجاوز غير القانوني ثم هناك التوقيع الصريح. هل يمكننا أن نتفق جميعًا على أن الأول هو الحد الأدنى المطلق الذي نتوقعه من هذه الكلمة الرئيسية؟

أقول هذا فقط لأن هناك طرقًا أخرى لفرض تواقيع الطريقة الصريحة حاليًا ، مثل الواجهات ، ولكن لا توجد حاليًا طريقة للتعبير صراحة عن نية التجاوز.

حسنًا ، لا توجد طريقة لفرض التوقيعات الصريحة على الطرق التي تم تجاوزها ، ولكن نظرًا لأن المترجم يفرض أن أي تغييرات في التوقيع "آمنة" على الأقل ، فيبدو أن هناك محادثة منفصلة يجب إجراؤها حول حل هذه المشكلة.

نعم وافقت. إذا كان علي الاختيار ، فإن حالة التجاوز المفقود / التجاوز غير القانوني هي المشكلة الأكثر أهمية التي يجب حلها.

لقد تأخرت قليلاً عن الحفلة ... أعتقد أن الهدف الأساسي من تنك سكريبت هو فرض القواعد في وقت الترجمة ، وليس في وقت التشغيل ، وإلا فسنستخدم جميعًا جافا سكريبت عاديًا. أيضًا ، من الغريب بعض الشيء استخدام الاختراقات / kludges للقيام بأشياء قياسية في العديد من اللغات.
هل يجب أن تكون هناك كلمة رئيسية override في كتابة نصية؟ أنا أؤمن بذلك بالتأكيد. هل يجب أن يكون إلزاميا؟ لأسباب تتعلق بالتوافق ، أود أن أقول إن سلوكه يمكن تحديده باستخدام وسيطة مترجم. هل يجب أن يفرض التوقيع الدقيق؟ أعتقد أن هذا يجب أن يكون مناقشة منفصلة ، لكن ليس لدي أي مشكلة مع السلوك الحالي حتى الآن.

يبدو أن السبب الأصلي لإغلاق هذا هو أننا لا نستطيع إدخال التغيير الفاصل الذي تحتاجه جميع الطرق التي تم تجاوزها إلى محدد override ، مما قد يجعل الأمر محيرًا نظرًا لأن الطرق التي لم يتم تمييزها بـ override يمكن أن تكون أيضًا في الحقيقة تكون تجاوزات.

لماذا لا يتم فرضها ، إما باستخدام خيار مترجم ، و / أو إذا كان هناك _ على الأقل طريقة واحدة_ تم وضع علامة عليها override في الفصل ، فيجب وضع علامة على جميع الطرق التي يتم تجاوزها على هذا النحو ، وإلا فهذا خطأ؟

هل يستحق الأمر إعادة فتح هذا وهو في الهواء؟

في الجمعة ، 8 أبريل 2016 الساعة 14:38 ، كتب Peter Palotas [email protected] :

يبدو أن السبب الأصلي لإغلاق هذا هو أننا لا نستطيع تقديمه
التغيير الفاصل الذي تحتاجه جميع الطرق التي تم تجاوزها
المحدد ، مما قد يجعل الأمر مربكًا نظرًا لأن الطرق غير مميزة بعلامة
يمكن أن يكون "التجاوز" تجاوزات في الواقع.

لماذا لا يتم فرضه ، إما باستخدام خيار مترجم ، و / أو إذا كان موجودًا في
طريقة "واحدة" على الأقل تم تمييزها بالتجاوز في الفئة ، ثم جميع الطرق الموجودة
يجب وضع علامة على التجاوزات على هذا النحو ، وإلا فهو خطأ؟

-
أنت تتلقى هذا لأنه تم ذكرك.
قم بالرد على هذا البريد الإلكتروني مباشرة أو قم بعرضه على GitHub
https://github.com/Microsoft/TypeScript/issues/2000#issuecomment -207434898

أقول من فضلك أعد فتح! سأكون سعيدًا بخيار المترجم.

نعم - يرجى إعادة الفتح

أعد فتح هذا!

لا يتطلب ES7 هذا التجاوز / التحميل الزائد لأساليب متعددة لها نفس الشيء
اسم؟
في 8 نيسان (أبريل) 2016 ، الساعة 10:56 صباحًا ، كتب "آرام الطيب" [email protected] :

نعم ، يرجى إعادة فتح هذا!

-
أنت تتلقى هذا لأنك مشترك في هذا الموضوع.
قم بالرد على هذا البريد الإلكتروني مباشرة أو قم بعرضه على GitHub
https://github.com/Microsoft/TypeScript/issues/2000#issuecomment -207466464

+1 - بالنسبة لي ، هذه هي أكبر فجوة في أمان النوع لدي في تطوير TypeScript يوميًا.

في كل مرة أقوم بتنفيذ طريقة دورة حياة React مثل "componentDidMount" أبحث عن صفحة توثيق التفاعل ذات الصلة وأنسخ / ألصق اسم الطريقة ، للتأكد من عدم وجود خطأ إملائي. أفعل ذلك لأنه يستغرق 20 ثانية في حين أن الأخطاء الناتجة عن خطأ مطبعي غير مباشرة ودقيقة ، ويمكن أن تستغرق وقتًا أطول لتعقبها.

فئة ذات 5 طرق ، منها 3 تم وضع علامة تجاوز عليها ، لا تعني أن الطريقتين الأخريين ليسا تجاوزات. لتبرير وجوده ، سيحتاج المعدِّل حقًا إلى تقسيم العالم بشكل أكثر نظافة من ذلك.

إذا كان هذا هو مصدر القلق الرئيسي ، فاتصل بالكلمة الأساسية check_override لتوضيح أنك تختار تجاوز التحقق ، ومن خلال ضمنيًا ، لا يتم تحديد الطرق الأخرى بدلاً من _لا يتم تجاوزها_.

ماذا عن استخدام implements. ؟ أن تكون شيئًا كهذا:

class MyComponent extends React.Component<MyComponentProps, void>{
    implements.componentWillMount(){
        //do my stuff
    }
}

لهذه الصيغة بعض المزايا:

  • يستخدم كلمة رئيسية موجودة بالفعل.
  • بعد كتابة implements. ، يتمتع IDE بفرصة ممتازة لإظهار نافذة منبثقة للإكمال التلقائي.
  • توضح الكلمة الأساسية التي يمكن استخدامها لفرض التحقق من الأساليب المجردة للفئات الأساسية ولكن أيضًا الواجهات المطبقة.
  • بناء الجملة غامض بدرجة كافية ليكون قابلاً للاستخدام أيضًا في الحقول ، مثل هذا:
class MyComponent<MyComponentProps, MyComponentState> {
    implements.state = {/*auto-completion for MyComponentState here*/};

    implements.componentWillMount(){
        //do my stuff
    }
}

ملاحظة: بدلاً من ذلك ، يمكننا استخدام base. ، إنه فارز أكثر سهولة ، ولكن ربما يكون أكثر إرباكًا (تحديد أو استدعاء؟) والمعنى غير متوافق مع تنفيذ الواجهة. مثال:

class MyComponent<MyComponentProps, MyComponentState> {
    base.state = {/*auto-completion for MyComponentState here*/};

    base.componentWillMount(){ //DEFINING
        //do my stuff
        base.componentWillMount(); //CALLING
        //do other stuff
    }
}

لا أعتقد أن implements يغطي جميع حالات الاستخدام بشكل كافٍ
المذكورة سابقًا وهي غامضة بعض الشيء. لم يعد يعطي أي شيء
معلومات إلى IDE لم يستطع override أيضًا ، لذلك يبدو أن
من المنطقي التمسك بالمصطلحات التي استخدمتها العديد من اللغات الأخرى
تحقيق نفس الشيء.
يوم الأربعاء ، 13 أبريل 2016 الساعة 19:06 ، كتب Olmo [email protected] :

ماذا عن استخدام الأدوات.؟ أن تكون شيئًا كهذا:

تقوم الفئة MyComponent بتوسيع React.Component{
implements.componentWillMount () {
// افعل أشيائي
}
}

لهذه الصيغة بعض المزايا:

  • يستخدم كلمة رئيسية موجودة بالفعل.
  • بعد الكتابة. IDE لديه فرصة ممتازة لإظهارها
    طريقة الإكمال التلقائي.
  • توضح الكلمة الأساسية التي يمكن استخدامها لفرض التحقق من الملخص
    طرق الفئات الأساسية ولكن أيضًا واجهات مطبقة.
  • بناء الجملة غامض بدرجة كافية ليكون قابلاً للاستخدام أيضًا في الحقول ، مثل
    هذه:

فئة MyComponent{
implements.state = {/ _auto-complete لـ MyComponentState here_ /} ؛

implements.componentWillMount(){
    //do my stuff
}

}

-
أنت تتلقى هذا لأنه تم ذكرك.
قم بالرد على هذا البريد الإلكتروني مباشرة أو قم بعرضه على GitHub
https://github.com/Microsoft/TypeScript/issues/2000#issuecomment -209571753

تكمن مشكلة التجاوز في أنه بمجرد استخدام نفس الكلمة الرئيسية يكون لديك نفس التوقعات:

  • يجب أن يكون إلزاميًا ، على الأقل إذا كانت الطريقة abstract .
  • لقد فقدت بعد ذلك بعض الكلمات الرئيسية virtual للتعليق على الأساليب التي من المفترض أن يتم تجاوزها ولكن لها سلوك افتراضي.

أعتقد أن فريق TS لا يريد إضافة الكثير من الأشياء المهمة إلى اللغة ، وأعتقد أنها فكرة جيدة.

باستخدام implements. لديك بنية خفيفة للحصول على الفوائد الرئيسية: الإكمال التلقائي واسم التحقق من وقت الترجمة ، دون اختراع كلمات رئيسية جديدة أو زيادة عدد المفاهيم.

له أيضًا ميزة العمل للفئات والواجهات وللأساليب (في النموذج الأولي) أو الحقول المباشرة.

تمت مناقشة طرق جعل override إلزاميًا بالفعل في الخيط والحلول لا تختلف عن الميزات الأخرى التي ينفذها المترجم.

الكلمة الأساسية virtual ليست منطقية حقًا في سياق اللغة ولم يتم تسميتها بطريقة بديهية للأشخاص الذين لم يستخدموا لغات مثل C ++ من قبل. ربما يكون الحل الأفضل لتوفير هذا النوع من الحراسة ، إذا لزم الأمر ، هو الكلمة الأساسية final .

أوافق على عدم إضافة ميزات اللغة على عجل والتي يمكن أن تخلق "أمتعة" ، ولكن override يسد ثغرة شرعية في نظام الوراثة التي تعتبرها العديد من اللغات الأخرى ضرورية. يتم تحقيق وظائفها بشكل أفضل باستخدام كلمة رئيسية جديدة ، وبالتأكيد عندما تقترح الطرق البديلة تغييرات أساسية في بناء الجملة.

ماذا عن تعيين الحقول الموروثة مقابل إعادة تعريف الحقول الجديدة؟ رد فعل state مثال جيد.

أو تنفيذ أساليب واجهة اختيارية؟

يمكن استخدام التجاوز في الحقول ، إذا تم إعادة الإعلان عنها
في جسم الفئة الفرعية. لا يتم تجاوز طرق الواجهة الاختيارية
خارج نطاق ما نتحدث عنه هنا.

في الخميس ، 14 أبريل 2016 الساعة 11:58 ، كتب Olmo [email protected] :

ماذا عن تعيين الحقول الموروثة مقابل إعادة تعريف الحقول الجديدة؟ رد فعل الدولة
هو مثال جيد.

أو تنفيذ أساليب واجهة اختيارية؟

-
أنت تتلقى هذا لأنه تم ذكرك.
قم بالرد على هذا البريد الإلكتروني مباشرة أو قم بعرضه على GitHub
https://github.com/Microsoft/TypeScript/issues/2000#issuecomment -209879217

لا يتم تجاوز طرق الواجهة الاختيارية ، لذا فهي خارج نطاق ما نتحدث عنه هنا.

تعتبر طرق الواجهة الاختيارية مفهومًا فريدًا إلى حد ما بالنسبة إلى TypeScript بقدر ما أعرف ، وأعتقد أن تطبيق TypeScript لـ "التخطي" يجب أن ينطبق عليها.

في حالة توابع دورة حياة React مثل componentDidMount ، فهذه طرق واجهة اختيارية ليس لها تنفيذ في الطبقة الفائقة.

في حالة توابع دورة حياة React مثل componentDidMount ، فهذه طرق واجهة اختيارية ليس لها تنفيذ في الطبقة الفائقة.

بالضبط ، حتى لا يتجاوزوا أي شيء. أعتقد أننا نشعر بالارتباك بشأن ما تهدف الكلمة الرئيسية override إلى توفيره هنا ، لأنه إذا كنت تبحث فقط عن طريقة لتحسين تلميحات / تحسس الكود ، فهناك طرق أخرى يمكن تحقيقها دون إضافة كلمات رئيسية جديدة للغة.

يا رفاق ، مع كل الاحترام الواجب ، هل يمكننا من فضلكم أن نبقى مركزين. أعتقد أن مناقشة الكلمات الرئيسية البديلة تأتي بنتائج عكسية ، خاصة وأن المشكلة بدأت بطلب محدد.

هل يمكننا أن نتفق جميعًا على أننا بحاجة إلى override ونطلب بلطف من فريق Typescript إضافته أو إسقاط الطلب والسماح بإغلاق المشكلة؟

أعتقد أنه من المفيد دائمًا طرح الطلب في سياق ماهية المشكلة ، فهناك دائمًا طرق مختلفة لحل المشكلة. كانت نقطة الخلاف مع التجاوز حول كونها إلزامية أم لا (يشير البعض إلى أنها ليست إلزامية في C ++). المشكلة التي أشار إليها الكثير من الأشخاص هي الحصول على الاسم الصحيح للوظائف القابلة للتجاوز (والتي قد يتم تنفيذها أو لا يتم تنفيذها في الفئة الفائقة) - وظائف دورة حياة React هي المشكلة الرئيسية.

إذا لم ينجح التجاوز ، فربما يجب علينا إغلاق هذه المشكلة وفتح نقطة أكثر عمومية حول هذه المشكلة. تكمن المشكلة العامة هنا في أن عقد الواجهة مع الطبقة الفائقة غير مكتوب وغير محدد ، وهذا يؤدي إلى تعطل المطورين وسيكون أمرًا رائعًا إذا كان بإمكان TS أو الأدوات المساعدة.

armandn لا أعتقد أن افتقارنا إلى الإغلاق أو دقة اقتراحنا هو ما يحافظ على رفض الطلب ، ولكن الاختلافات بين دلالات C # و TS:

تجاوز الطريقة الأساسية C #:

  • يتطلب الكلمة الأساسية override
  • يقوم بإنشاء سجل جديد على VTable
  • عمليات التحقق من الملخص الإلزامي / الظاهري في الطريقة الأساسية
  • الشيكات للتوقيع المتطابق
  • IDE: يتم تشغيل الإكمال التلقائي بعد كتابة التجاوز

تنفيذ واجهة C #:

  • لا توجد كلمة أساسية ضرورية ، ولكن يمكن تنفيذ واجهة صريحة باستخدام اسم الواجهة.
  • قم بإنشاء سجل واجهة جديد في VTable.
  • الشيكات للتوقيع المتطابق
  • IDE: QuickFix لتنفيذ الواجهة / واجهة التنفيذ بشكل صريح.

لذا فإن السلوك في C # مختلف تمامًا اعتمادًا على ما إذا كنت تسأل عن الفصول أو الواجهات ، ولكن على أي حال تحصل على الفوائد الثلاثة الرئيسية:

  1. التحقق من وجود توقيع متطابق
  2. التحقق من أن الطريقة يمكن / يجب تجاوزها (خطأ إملائي في اسم الطريقة)
  3. IDE يدعم كتابة الوظيفة

مع دلالات مختلفة قليلاً ، لدينا بالفعل 1) في TS ، للأسف 2) و 3) مفقودة. السبب في رفض override ، في رأيي ، هو أن بناء جملة مشابه يفترض سلوكًا مشابهًا ، وهذا غير مرغوب فيه للأسباب التالية:

لا يعني الفصل الذي يحتوي على 5 طرق ، منها 3 تم وضع علامة تجاوز عليها ، أن الطريقتين الأخريين ليسا تجاوزات.

يحدث أن لدينا بالفعل 1) 2) و 3) عند كتابة _object literals_ ، ولكن ليس عند كتابة أعضاء الفصل الذين يقومون بتوسيع الفئات الأخرى أو تنفيذ واجهات.

مع وضع هذا في الاعتبار ، أعتقد أنه يمكننا جميعًا الاتفاق على هذه الدلالات:

  • يجب أن تكون _keyword_ غامضة بدرجة كافية لتجنب مشكلة "العالم المنقسم". (على سبيل المثال: check أو super. أو MyInterface. )
  • لا توجد عمليات تحقق لـ abstract/virtual ولكن يتم التحقق من وجود العضو في الفئة الأساسية / الواجهات المطبقة. (فائدة 2)
  • عمليات التحقق من توافق التوقيع المماثل بدرجة كافية كما يفعل TS حتى الآن. (فائدة 1)
  • IDE: لتشغيل الإكمال التلقائي بعد الكلمة الأساسية. (فائدة 3)

بالإضافة إلى ذلك ، أعتقد أن هذين الاثنين ضروريان لاستكمال الحل *:

  • يجب أن يعمل مع الفئات والواجهات ، لأن TS يعتمد بشكل أساسي على الواجهة والفئات هي اختصار للوراثة النموذجية. ضروري بالنظر إلى طرق الواجهة الاختيارية.
  • يجب أن تعمل مع الطرق والحقول ، لأن الأساليب هي مجرد وظائف موجودة في الحقل.

هاتان النقطتان مفيدتان في حالة الاستخدام الواقعية جدًا لتطبيق مكون React:

ج #
فئة MyComponent{
implements.state = {/ auto-complete لـ MyComponentState هنا /} ؛

implements.componentWillMount(){
    //do my stuff
}

}
""

الحل المستند إلى التعليقات التوضيحية من RyanCavanaugh ليس كافيًا للأسباب التالية:

  • لن تعمل مع الواجهات.
  • لن تعمل مع الحقول.
  • بافتراض وجود بنية تحتية تشبه Roslyn للمساعدة في الأدوات ، لا توجد طريقة لإضافة QuickFix بعد كتابة قائمة الإكمال التلقائي بعد كتابة @override .

بالنظر إلى الدلالات ، فإن الأمر يتعلق فقط باختيار الصيغة الصحيحة. إليك بعض البدائل:

  • override componentWillMount() : بديهي لكنه مضلل.
  • check componentWillMount() : صريح ولكنه يستهلك الكلمات الرئيسية.
  • super componentWillMount() :
  • implements componentWillMount() :
  • super.componentWillMount() :
  • implements.componentWillMount() :
  • this.componentWillMount() :
  • ReactComponent.componentWillMount() :

آراء؟

olmobrutall ملخص جيد. زوجان من النقاط:

سيكون خيار كلمة رئيسية واحدة (مأخوذ من C #) جديدًا - يشير إلى فتحة جديدة:

class MyComp extends React.Component<IProps,IState> {
...
    new componentWillMount() { ... }
    componentWillMount() { ...} // would compile, maybe unless strict mode is enabled
    new componentwillmount() { ... } <-- error

نقطتي الأخرى هي مسألة الطبيعة الإلزامية لاستخدام ما ورد أعلاه. كعقد بين الفئة الرئيسية الأصلية والفئة المشتقة ، فمن المنطقي تحديد نقاط الواجهة حيث يكون بناء الجملة أعلاه صحيحًا. هذه حقًا نقاط امتدادات داخلية للفئة الممتازة ، لذلك هناك شيء مثل:

class Component<P,S> {
    extendable componentWillMount() {...}
}

وينطبق الشيء نفسه على الواجهة أيضًا.

شكرا :)

ماذا عن مجرد كتابة this ؟

class MyComponent<MyComponentProps, MyComponentState> {
    this.state = {/*auto-completion for MyComponentState here*/};

    this.componentWillMount(){
        //do my stuff
    }
}

حول extendable ، يوجد بالفعل abstract في TS 1.6 ، وإضافة افتراضية ربما تخلق مشكلة العالم المنقسم مرة أخرى؟

حسنًا ، لقد فكرت في فكرة مجردة ولكن قد يكون هناك تطبيق في الفئة الممتازة ، لذا فهو غير منطقي حقًا. وبالمثل مع الوضع الظاهري ، حيث قد يعني ذلك أن الأعضاء غير الافتراضيين ليسوا افتراضيين - وهذا أمر مضلل.

this. يعمل ، أعتقد أنه يمكنك حتى (كصيغة طويلة):

   this.componentWillMount = () => { }

المشكلة الوحيدة في هذا هو أنه يجب تحديد نطاقه فقط لنقاط الامتداد المخصصة ، وليس كل أعضاء الفئة الأساسية.

ما الذي يجري...

TypeScript ليس ولم يكن أبدًا إصدار جافا سكريبت لـ C #. لذا فإن السبب ليس لأن الوظيفة المقترحة تختلف عن دلالات C #. كانت أسباب الإغلاق كما ذكرها رايان ثم أوضحها دانيال آر

كما أوضح ريان ، تكمن المشكلة في أن وضع علامة على طريقة ما على أنها تجاوز لا يعني أن طريقة أخرى ليست تجاوزًا. الطريقة الوحيدة التي يكون من المنطقي أن يتم تمييز جميع التجاوزات بكلمة رئيسية تجاوز (والتي إذا طلبناها ، ستكون تغييرًا مفاجئًا).

ومع ذلك ، ما زلت مستمرة في حل مشاكلك حول الإكمال التلقائي. لا يحتاج الإكمال التلقائي إلى كلمة رئيسية جديدة في اللغة لتزويدك باقتراحات محسنة.

كان سبب وجود هذا الخيط هو الحصول على أخطاء في المترجم عندما يتم تجاوز وظيفة بشكل غير قانوني أو عندما يتم الإعلان عن طريقة ما على أنها تجاوز ولكنها لم تتجاوز في الواقع وظيفة في فئة أساسية. إنه مفهوم بسيط ومُحدد جيدًا عبر العديد من اللغات والذي يدعم أيضًا معظم إن لم يكن المزيد من ميزات اللغة المطبوعة التي يجب أن تعرضها. لا تحتاج إلى حل جميع مشاكل العالم ، بل تحتاج فقط إلى أن تكون الكلمة الأساسية للتجاوز ، للتخطي.

أظهر المزيد من الأشخاص منذ ذلك الحين اهتمامًا بالاقتراح الأصلي ، لذلك دعنا نتمسك بالموضوع الذي أثيرت المشكلة من أجله وطرح قضايا جديدة لأفكار جديدة.

TypeScript ليس ولم يكن أبدًا إصدار جافا سكريبت لـ C #.

أقارنها بـ C # كلغة مرجعية ، لأنني أفترض أن هذا هو السلوك الذي تفترضه يا رفاق.

لا يحتاج الإكمال التلقائي إلى كلمة رئيسية جديدة في اللغة لتزويدك باقتراحات محسنة.

كيف تقترح أن يتم تشغيله بعد ذلك؟ إذا عرضت مربع تحرير وسرد للإكمال التلقائي عند كتابة اسم عشوائي في سياق فئة ، فسيكون ذلك مزعجًا للغاية عندما نريد فقط إعلان حقل أو طريقة جديدة.

كان سبب وجود هذا الخيط هو الحصول على أخطاء في المترجم عندما يتم تجاوز وظيفة بشكل غير قانوني أو عندما يتم الإعلان عن طريقة ما على أنها تجاوز ولكنها لم تتجاوز في الواقع وظيفة في فئة أساسية.

لقد قمت بتضمين حالة الاستخدام هذه تمامًا ، ضمن الميزة 2 .

لا تحتاج إلى حل جميع مشاكل العالم ، بل تحتاج فقط إلى أن تكون الكلمة الأساسية للتجاوز ، للتخطي.

لذا فإن اقتراحك هو إصلاح "خطوة واحدة في كل مرة" بدلاً من التراجع خطوة واحدة والنظر في المشكلات المماثلة / ذات الصلة ؟. قد تكون هذه فكرة جيدة لـ Scrum Board ولكن ليس لتصميم اللغات.

سبب تحفظهم بشأن إضافة الكلمة الرئيسية هو أنهم لا يستطيعون إزالة الميزات من اللغة.

بعض أخطاء التصميم في C # بسبب عدم الاكتمال:

  • التباين المشترك / التباين المقابل للصفيف غير الآمن.
  • var يعمل فقط مع أنواع المتغيرات ، وليس للمعلمات العامة التلقائية أو أنواع الإرجاع. Mayble auto ستكون كلمة رئيسية أفضل كما في C ++؟

فقط حاول استخدام React لمدة ثانية وسترى الجانب الآخر من الصورة.

يعد تجاوز طريقة تم تنفيذها بالفعل في فئة أساسية وتنفيذ طريقة واجهة _ شيئين مختلفين تمامًا _. لذا ، نعم ، ما يتم اقتراحه هو إصلاح أحد هذه السيناريوهات باستخدام كلمة رئيسية مخصصة بدلاً من محاولة الخروج ببعض الكلمات الرئيسية للجيش السويسري.

ما فائدة الكلمة الرئيسية التي يمكن أن تعني أيًا من 3 أشياء مختلفة للمطورين الذين يقرؤون بعض التعليمات البرمجية لأول مرة؟ إنه أمر غامض ومربك ، خاصة إذا كنت تتحدث عن استخدام كلمة رئيسية مثل this والتي تقوم بالفعل بأشياء أخرى (غير مرتبطة تمامًا!) في اللغة - لا يمكن أن تكون أكثر عمومية ، فهي عديمة الفائدة تقريبًا.

إذا كان اهتمامك الرئيسي هو الإكمال التلقائي ، فإن المحررين لديهم معلومات كافية _now_ ليتمكنوا من اقتراح طرق من الفئات الأساسية والواجهات المطبقة "أثناء الكتابة".

يعد تجاوز طريقة تم تنفيذها بالفعل في فئة أساسية وتنفيذ طريقة واجهة شيئين مختلفين تمامًا.

بشكل عام ، نعم ، لكننا لا نتحدث عن تنفيذ أي طريقة للواجهة. نحن نتحدث عن طريقة واجهة اختيارية _ حيث تقوم الفئة الرئيسية بتنفيذ الواجهة_. في هذه الحالة ، يمكنك القول أن 1) تسمح الواجهة بتنفيذ الطريقة على أنها undefined ، 2) الفئة الأصلية بها تطبيق غير محدد ، و 3) أن الفئة الفرعية تتخطى التنفيذ غير المحدد باستخدام طريقة التنفيذ.

olmobrutall أعتقد أن تعليقك حول تصميم اللغات وكيف أنها ليست لوحة سكرم هو خدمة ذاتية إلى حد ما. لقد رأيت حوالي أربعة تحديثات لـ TS في غضون أقل من عام.

إذا كان تصميم اللغة مدروسًا جيدًا كما تشير ضمنيًا أنه يجب أن يكون ، فسيكون هناك بالفعل مستند خاص بلغة تخبرنا بالضبط كيف يجب أن تعمل الإلغاءات ، وربما لا نجري هذه المحادثة.

لا أدلي بهذا التعليق مع أي تشويه لمطوري / مصممي TS ، لأن TS ممتاز بالفعل وسأخشى الاضطرار إلى استخدام JS القياسي بدلاً من ذلك.

نعم TS ليست C # وليست C ++. لكن عددًا من اللغات اختارت الكلمة الأساسية للتجاوز لتلبية الأهداف التي تمت مناقشتها هنا ، لذا يبدو أنه من غير المجدي اقتراح بناء جملة غريب تمامًا.

يبدو أن القضية الرئيسية هي عدم الرغبة في إدخال تغيير جذري. الجواب البسيط هو علم المترجم ، نهاية القصة. بالنسبة لبعض الأشخاص مثلي ، لا فائدة من استخدام كلمة رئيسية اختيارية للإلغاء. بالنسبة للآخرين ، يريدون تجميل الكود بشكل تدريجي. علم المترجم يحل المعضلة.

الاختلافات في التوقيع هي محادثة مختلفة. تبدو الكلمة الأساسية الجديدة غير ضرورية لأن JS لا يمكنها دعم طرق متعددة تحمل نفس الاسم (ما لم ينشئ TS أسماء مشوهة مشتقة من التوقيع a'la C ++ ، وهو أمر مستبعد جدًا).

لقد رأيت حوالي أربعة تحديثات لـ TS في غضون أقل من عام.

لا أقصد أنك لا تستطيع أن تكون سريعًا وتتكرر بسرعة. أنا سعيد مثل أي شخص آخر أن ES6 و TS يتطوران بسرعة. ما أعنيه هو أنه عليك محاولة التنبؤ بالمستقبل ، لتجنب وضع اللغة في طريق مسدود.

يمكنني الموافقة على استخدام الكلمة الرئيسية override . مع الحجج المناسبة حتى إبقاء الحقول والواجهات خارج النطاق ، لكن لا يمكنني أن أتفق مع حجة "دعنا نحافظ على تركيزنا ونحل هذه المشكلة بالذات بالطريقة التي تفعلها اللغات الأخرى دون التفكير كثيرًا".

لكن عددًا من اللغات اختارت الكلمة الأساسية للتجاوز لتلبية الأهداف التي تمت مناقشتها هنا ، لذا يبدو أنه من غير المجدي اقتراح بناء جملة غريب تمامًا.

لا تحتوي أي من هذه اللغات على ميراث نموذجي أو طريقة اختيارية (طرق ليست مجردة أو افتراضية ، لا يمكن أن تكون موجودة في وقت التشغيل) ، وهذه مشكلات ذات صلة يجب مناقشتها (وربما تجاهلها) قبل الالتزام.

بعبارة أخرى: لنفترض أننا نفعل ما يبدو أنك تقترحه وننفذ التجاوز دون التفكير كثيرًا. ثم أنا ، أو أي شخص آخر يستخدم TSX ، يضيف مشكلة لماذا override لا يعمل مع مكونات React. ما هي خطتك؟

بشكل عام ، نعم ، لكننا لا نتحدث عن تنفيذ أي طريقة للواجهة. نحن نتحدث عن طريقة واجهة اختيارية حيث يقوم الفصل الرئيسي بتنفيذ الواجهة.

لا يهم مكان إعداد الواجهة ، فالحقيقة هي أنهما ليسا نفس الشيء ولذا لا يجب مشاركة كلمة رئيسية لأن _intent_ البرنامج غير واضح.

يمكنك ، على سبيل المثال ، تجاوز طريقة تم تنفيذها لتوافق الواجهة في فئة أساسية ؛ إذا كنا سنضع كل بيضنا في كلمة رئيسية واحدة لهذين الشيئين المختلفين ، فكيف يمكن لأي شخص أن يعرف ما إذا كان هذا هو الإعلان الأولي لهذه الوظيفة أو تجاوز واحد تم تحديده مسبقًا في فئة أساسية؟ لن تفعل ذلك ، ولن يكون من الممكن معرفة ذلك بدون مزيد من الفحص للفئة الأساسية - والتي قد تكون في ملف تابع لجهة خارجية .d.ts مما يجعل العثور عليه كابوسًا مطلقًا ، اعتمادًا على مدى عمق في سلسلة الوراثة ، تم تنفيذ الوظيفة في الأصل.

بعبارة أخرى: لنفترض أننا نفعل ما يبدو أنك تقترحه وننفذ التجاوز دون التفكير كثيرًا. ثم أنا ، أو أي شخص آخر يستخدم TSX ، يضيف مشكلة لماذا لا يعمل التخطي مع مكونات React. ما هي خطتك؟

لماذا يحتاج هذا إلى إصلاح React؟ إذا كانت لدى React مشكلة مختلفة عما يحاول هذا حلها ، فلا يمكنني أن أفهم لماذا يحتاج override لإصلاحها؟ هل حاولت فتح مشكلة أخرى لاقتراح شيء ما يمكن عمله بشأن تنفيذ الواجهة؟

لا أوافق على أنه لم يتم وضع تفكير كافٍ في هذا الأمر. نقترح تقنية مجربة ومختبرة كانت ناجحة في كل لغة أخرى يمكنني التفكير فيها وتم تنفيذها.

الحقيقة هي أنهما ليسا نفس الشيء ولذا لا يجب مشاركة كلمة رئيسية لأن الهدف من البرنامج غير واضح.

غير صحيح؟ انظر إلى هذين التعريفين البديلين لـ BaseClass

class BaseClass {
     abstract myMethod(); 
}
interface ISomeInterface {
     myMethod?(); 
}

class BaseClass extends ISomeInterface {
}

ثم في التعليمات البرمجية الخاصة بك ، تقوم بما يلي:

ج #
فئة ConcreteClass {
تجاوز myMethod () {
// افعل اشياء
}
}

You think it should work in just one case and not in the other? The effect is going to be 100% identical in Javascript (creating a new method in ConcreteClass prototype), from the external interface and from the tooling perspective. 

Even more, maybe you want to capture `this` inside of the method, implementing it with a lambda (useful for React event handling). In this case you'll write something like this:

``` C#
class ConcreteClass {
    override myMethod = () => { 
         // Do stuff
    }
}

سيكون السلوك متطابقًا مرة أخرى إذا كانت الطريقة مجردة أو تأتي من الواجهة: أضف حقلاً في الفصل مع تطبيق lambda له. ولكن يبدو الأمر غريبًا بعض الشيء بالنسبة إلى override للحقل ، نظرًا لأنك تقوم فقط بتعيين قيمة.

لا دعنا نراه باستخدام super. (بناء الجملة المفضل لدي الآن ، لكنني منفتح على البدائل).

ج #
فئة ConcreteClass {
super.myMethod () {// طريقة في النموذج الأولي
// افعل اشياء
}

super.myMethod = () => {  //method in lambda
     // Do stuff
}

}
""

أصبح المفهوم الآن أبسط من الناحية المفاهيمية: يقول صفي الفائق أن هناك طريقة أو مجالًا ويمكن لفئة الخرسانة تحديدها في النموذج الأولي / تعيينها / قراءتها / تسميتها.

لماذا يحتاج هذا إلى إصلاح React؟

ليس مجرد رد فعل ، انظر إلى الزاوية:

بالطبع ، لا يُقصد تنفيذ معظم الواجهات ، ولا يتم تجاوز جميع الفئات ، ولكن هناك شيء واحد واضح: في الواجهات الأنواع أكثر أهمية من الفئات.

هل حاولت فتح مشكلة أخرى لاقتراح شيء ما يمكن عمله بشأن تنفيذ الواجهة؟

كيف اسميه؟ override للواجهة والحقول؟

نقترح تقنية مجربة ومختبرة كانت ناجحة في كل لغة أخرى يمكنني التفكير فيها.

اللغات التي تفكر فيها مختلفة تمامًا. لديهم ميراث قائم على VTable ثابت.

في Typescript ، تكون الفئة مجرد واجهة + وراثة تلقائية للنماذج الأولية للطرق. والطرق ما هي إلا حقول لها دالة بداخلها.

من أجل تكييف ميزة override مع TS ، يجب مراعاة هذين الاختلافين الأساسيين.

من فضلك @ kungfusheep بذل الجهد للتفكير في حل لمشكلتي. إذا كنت ترغب في إضافة كلمات رئيسية مختلفة وتنفيذها في مرحلة ثانية ، فلا بأس ، لكن خذ ثانية لتخيل كيف ينبغي أن تكون.

لا أستطيع التفكير في طريقة أخرى لقول ما قلته بالفعل. انهم ليسو نفس الشيء. إنهم متشابهون ، لكن ليسوا متشابهين. يرجى الاطلاع على هذا التعليق من قبل أحد مطوري TS RE: الكلمة الأساسية للقراءة فقط - https://github.com/Microsoft/TypeScript/pull/6532#issuecomment -179563753 - والتي تعزز ما أقوله.

أتفق مع الفكرة العامة ، لكن دعنا نرى في الممارسة:

class MyComponent extends React.Component<{ prop : number }, { value: string; }> {

    //assign a field defined in the base class without re-defining it (you want type-checking)
    assign state = { value : number}; 

    //optional method defined in an interface implemented by the base class    
    implement componentDidMount(){ 
    }

    //abstract method defined in the base class 
    override render(){  
    }
}

هذا يبدو مثل VB أو Cobol ، أليس كذلك؟

يبدو أنه من المنطقي ، على الأقل.

ضع في اعتبارك هذا المثال ، هل كانت هناك كلمة رئيسية تجاوز (أو واحدة فقط).

interface IDo {
    do?() : void;
}
class Component implements IDo {
    protected commitState() : void {
        /// do something
    }
    override public do() : void {
        /// base implements 'do' in this case
    }
}

الآن دعنا ننفذ المكون الخاص بنا باستخدام ما كتبناه للتو.

class MyComponent extends Component {
    override protected commitState(){
        /// do our own thing here
        super.commitState();
    }
    override do() : void {
        /// this is ambiguous. Am I implementing this from an interface or overriding a base method? I have no way of knowing. 
    }

}

طريقة واحدة لمعرفة نوع super :

  override do() : void {
        super.do(); // this compiles, if it was an interface then super wouldn't support `do`
    }

بالضبط! مما يعني أن التصميم خاطئ. لا ينبغي أن يكون هناك تحقيق يتعلق بقشط الكود ، بل يجب أن يكون منطقيًا عند قراءته.

هذا غامض. هل أقوم بتنفيذ هذا من واجهة أو تجاوز طريقة أساسية؟ ليس لدي أي وسيلة لمعرفة.

ما هو الفرق في الممارسة؟ يحتوي الكائن على مسافة واحدة فقط من الأسماء ويمكنك وضع لامدا واحدة فقط في الحقل do . لا توجد طريقة لتنفيذ واجهة بشكل صريح على أي حال. التنفيذ سيكون:

MyComponent.prototype.do = function (){
    //your stuff
}

بصرف النظر عما تكتبه.

لا يهم ما هو الناتج. قد تتخطى عن قصد أو عن غير قصد بعض الوظائف في فئة أساسية ، ولكن لا توجد نية في كلمة أساسية غامضة.

ما الخطأ أو السلوك غير المتوقع الذي سيتم حله من خلال وجود كلمتين رئيسيتين؟

تعال الآن يا صديقي. من الواضح أنك رجل ذكي. لقد قلت للتو "تجاوز بدون قصد بعض الوظائف في فئة أساسية" ؛ ألا يمكننا أن نستنتج من هذا أي سلوك غير متوقع من المحتمل أن يكون قد حدث للتو؟

لأكون واضحًا ، لا أقترح تحويل هذه المشكلة إلى اقتراح لكلمتين رئيسيتين. هذه المشكلة خاصة بالكلمة الأساسية للتجاوز - أي شيء آخر سيحتاج إلى اقتراح جديد ومناقشته الخاصة حول الدلالات.

لأكون واضحًا ، لا أقترح تحويل هذه المشكلة إلى اقتراح لكلمتين رئيسيتين. هذه المشكلة خاصة بالكلمة الأساسية للتجاوز - أي شيء آخر سيحتاج إلى اقتراح جديد ومناقشته الخاصة حول الدلالات.

لا يهم حقًا عدد القضايا التي يجب مناقشتها ، أو من الذي تأتي منه الفكرة. أنت تقترح تقسيم فكرتين مترابطتين ولا تفكر حتى في بناء جملة متسق.

الحجج المتعلقة بما إذا كنا بحاجة إلى كلمة رئيسية واحدة أو كلمتين أو ثلاث كلمات رئيسية تنتمي إلى هذا الموضوع ولم يتم الانتهاء منها (... ولكن تصبح متكررة). ثم ربما يمكننا مناقشة بناء الجملة في موضوع آخر (لأن الدلالات ستكون متطابقة على أي حال: P).

في المثال الخاص بي:

class MyComponent extends React.Component<{ prop : number }, { value: string; }> {

    //assign a field defined in the base class without re-defining it (you want type-checking)
    assign state = { value : number}; 

    //optional method defined in an interface implemented by the base class    
    implement componentDidMount(){ 
    }

    //abstract method defined in the base class 
    override render(){  
    }
}

لا تفعل كل assign و implement و override نفس الشيء تمامًا : تحقق من وجود الاسم (في الفئة الأساسية ، الواجهات المنفذة ، الواجهات المطبقة بواسطة القاعدة فئة ، إلخ ...).

إذا كان هناك تعارض في الاسم بين الفئات الأساسية وبعض الواجهات المنفذة ، فستتلقى خطأ في وقت الترجمة سواء باستخدام كلمة رئيسية 1 أو 2 أو بدون كلمة رئيسية على الإطلاق.

فكر أيضًا في الكائن الحرفي:

var mc = new MyComponent(); 
mc.state = null;
mc.componentDidMount =null;
mc.render = null;

باستخدام نفس الصيغة بالضبط ، يمكنني إعادة تعيين الحقول أو الطريقة بشكل مستقل إذا كانت تأتي من الفئة الأساسية أو تطبيقات الواجهة المباشرة أو الواجهات المطبقة في الفئة الأساسية.

لا تقم بالتخصيص والتنفيذ والإلغاء للقيام بنفس الشيء تمامًا: تحقق من وجود الاسم (في الفئة الأساسية ، والواجهات المنفذة ، والواجهات التي نفذتها الفئة الأساسية ، وما إلى ذلك ...).

لقد وصفت للتو 3 سيناريوهات مختلفة هناك ، لذا من الواضح أنها ليست هي نفسها. لدي شعور بإمكاني وصف سبب اختلافهم عنك طوال اليوم وما زلت جالسًا تجادل بأنهم ليسوا كذلك ، لذلك سأخرج من هذا الخط المحدد من المناقشة في الوقت الحالي. لا يوجد شيء يمكن قوله أن رجال TS ما زالوا يفكرون في هذا في الوقت الحالي على أي حال.

مع إغلاق # 6118 أعتقد أن هناك سببًا لمناقشة ما إذا كان يمكن معالجة المشاكل الموجودة هناك والمشكلات هنا في وقت واحد.

لم أكن على علم بـ https://github.com/Microsoft/TypeScript/pull/6118. تبدو الفكرة كبديل محتمل لإضافة override .

إذا فهمت المشكلة بشكل صحيح ، فالمسألة هي أنه يمكن أن يكون لديك أكثر من فئة / واجهات أساسية واحدة مع تصريحات أعضاء متوافقة ولكن ليست متطابقة ، ويجب أن تكون موحدة عند التهيئة في فئة فرعية بدون نوع.

بدون فكرة جيدة عن العواقب ، سأكون سعيدًا إذا:

  • سينتج العضو خطأ وقت ترجمة يتطلب إعلان نوع صريح على فئة تابعة
  • سيأخذ العضو تقاطع جميع الأنواع الممكنة.

الأهم من ذلك هو أن يكون لدى IMO طريقة ما لتشغيل الإكمال التلقائي عند كتابة أعضاء الفصل (Ctrl + Space). عندما يكون المؤشر داخل الفصل مباشرة ، يمكنك تحديد أعضاء جدد لإعادة تعريف الأعضاء الموروثة ، لذلك لا يمكن أن يكون الإكمال التلقائي عدوانيًا للغاية ، ولكن مع التشغيل اليدوي ، يجب أن يكون السلوك جيدًا.

بخصوص تعليقات RyanCavanaugh :

نحن بالتأكيد نفهم حالات الاستخدام هنا. المشكلة هي أن إضافتها في هذه المرحلة في اللغة تضيف مزيدًا من الالتباس أكثر مما تزيله. لا يعني الفصل الذي يحتوي على 5 طرق ، منها 3 تم وضع علامة تجاوز عليها ، أن الطريقتين الأخريين ليسا تجاوزات. لتبرير وجوده ، سيحتاج المعدِّل حقًا إلى تقسيم العالم بشكل أكثر نظافة من ذلك.

كتابة متغير كـ any لا يعني أن متغيرًا آخر هو أو ليس كذلك any . ولكن ، هناك مترجم ثابت --no-implicit-any لفرض أننا نعلنه صراحة. يمكن أن يكون لدينا أيضًا --no-implicit-override ، والذي يمكنني تشغيله إذا كان متاحًا.

إن امتلاك كلمة رئيسية override يتم استخدامها يمنح المطورين قدرًا هائلاً من البصيرة عند قراءة التعليمات البرمجية التي لم يكونوا على دراية بها ، كما أن القدرة على فرضها ستمنح بعض التحكم الإضافي في وقت التجميع.

لجميع مستخدمي +1 - هل يمكنك التحدث عما هو غير كافٍ بشأن حل الديكور الموضح أعلاه؟

هل هناك أي طريقة يكون فيها المصمم حلاً أفضل من كلمة رئيسية تجاوز؟ هناك العديد من الأسباب التي تجعله أسوأ: 1) يضيف وقت التشغيل الإضافي مهما كان صغيراً ؛ 2) ليس فحص وقت الترجمة ؛ 3) يجب أن أدرج هذا الرمز في كل مكتبة من مكتباتي ؛ 4) لا توجد طريقة لهذا لالتقاط الوظائف التي تفتقد الكلمة الأساسية للتجاوز.

دعنا نعطي مثالا. لدي مكتبة بها ثلاثة صفوف: ChildA ، ChildB ، Base . لقد قمت بإضافة طريقة doSomething() إلى ChildA و ChildB . بعد بعض إعادة الهيكلة ، أضفت بعض المنطق الإضافي ، وقمت بتحسين ونقل doSomething() إلى فئة Base . في غضون ذلك ، لدي مشروع آخر يعتمد على مكتبتي. هناك لدي ChildC doSomething() . لا توجد طريقة عندما أقوم بتحديث التبعيات الخاصة بي لاكتشاف أن ChildC يتخطى الآن ضمنيًا doSomething() ، ولكن بطريقة غير مُحسَّنة تفتقد أيضًا بعض الشيكات. هذا هو السبب في أن @overrides الديكور لن يكون كافيًا أبدًا.

ما نحتاجه هو كلمة رئيسية override --no-implicit-override وعلامة المترجم $ # $ 19 $ # $.

ستساعدني الكلمة الرئيسية override كثيرًا لأنني أستخدم في مشروعي تسلسلًا هرميًا بسيطًا للفئات الأساسية لإنشاء جميع مكوناتي. تكمن مشكلتي في حقيقة أن هذه المكونات قد تحتاج إلى الإعلان عن طريقة لاستخدامها في مكان آخر ، وقد يتم تعريف هذه الطريقة أو لا يتم تعريفها في فئة الأصل وقد تقوم أو لا تقوم بالفعل بأشياء أحتاجها.

على سبيل المثال ، لنفترض أن دالة validate تأخذ فئة باستخدام طريقة getValue() كمعامل. لإنشاء هذه الفئة ، يمكنني أن أرث فئة أخرى يمكنها بالفعل تحديد طريقة getValue() هذه ، لكن لا يمكنني معرفة ذلك حقًا ما لم ألقي نظرة على الكود المصدري الخاص بها. ما أفعله غريزيًا هو تطبيق هذه الطريقة في صفي ، وطالما أن التوقيع صحيح ، فلن يخبرني أحد بأي شيء.

لكن ربما لم يكن علي فعل ذلك. تفترض جميع الاحتمالات التالية أنني قمت بتجاوز ضمني:

  1. حددت الفئة الأساسية هذه الطريقة بالفعل مثلما فعلت ، لذلك كتبت وظيفة من أجل لا شيء. ليست مشكلة كبيرة.
  2. أعدت كتابة الوظيفة بشكل غير صحيح ، معظم الوقت لأنني نسيت القيام ببعض الأشياء غير الواضحة التي تم التعامل معها بالفعل من قبل الفئة الأساسية. ما أحتاجه حقًا هنا هو الاتصال بـ super في التجاوز الخاص بي ، لكن لم يقترحني أحد للقيام بذلك.

إن وجود كلمة رئيسية إلزامية للتجاوز كان سيخبرني "مرحبًا ، أنت تجاوز ، لذلك ربما يجب عليك التحقق من الشكل الأصلي للطريقة قبل القيام بالأشياء الخاصة بك". من شأنه أن يحسن تجربتي مع الميراث بشكل كبير.

بالطبع ، كما هو مقترح ، يجب وضعه تحت علامة --no-implicit-override لأنه سيكون تغييرًا مفاجئًا وأن معظم الناس لا يهتمون كثيرًا بكل هذا.

أنا أحب المقارنة بين eggers المصنوع من any و --no-implicit-any لأنه من نفس النوع من التعليقات التوضيحية وسيعمل بنفس الطريقة تمامًا.

olmobrutall كنت أبحث في بعض حديثك عن تجاوز الأساليب المجردة وطرق الواجهة.

إذا كان رأيي ، يشير override إلى وجود super. لا يمكن استدعاء أي من الأساليب المجردة أو الأساليب المحددة في واجهة عبر استدعاء super. ، وبالتالي لا ينبغي تجاوزه ( لا يوجد شيء لتجاوزه). بدلاً من ذلك ، إذا فعلنا شيئًا أكثر وضوحًا لهذه الحالات ، فيجب أن تكون كلمة رئيسية implements . ولكن ، سيكون ذلك مناقشة ميزة منفصلة.

ها هي المشاكل كما أراها مرتبة من الأكثر أهمية على الأقل. هل فوت اي شيء؟

عدم التجاوز

من السهل _تفكر_ أنك تتجاوز طريقة فئة أساسية عندما لا تكون:

class Base {
  hasFilename(f: string) { return true; }
}
class Derived extends Base {
  // oops
  hasFileName(f: string) { return false; }
}

ربما تكون هذه هي المشكلة الأكبر.

عدم التنفيذ

يرتبط هذا ارتباطًا وثيقًا ، خاصةً عندما تحتوي الواجهة المنفذة على خصائص اختيارية:

interface NeatMethods {
  hasFilename?(f: string): boolean;
}
class Mine implements NeatMethods {
  // oops
  hasFileName(f: string) { return false; }
}

هذه مشكلة أقل أهمية من الفشل في التجاوز ، لكنها لا تزال سيئة.

تجاوز عرضي

من الممكن تجاوز طريقة الفئة الأساسية دون إدراك ذلك

class Base {
  hasFilename(f: string) { return true; }
}
class Derived extends Base {
  // I didn't know there was a base method with this name, so oops?
  hasFilename(f: string) { return true; }
}

يجب أن يكون هذا نادرًا نسبيًا لمجرد أن مساحة أسماء الطرق المحتملة كبيرة جدًا ، واحتمالات كتابة توقيع متوافق _و_ لا تعني أنك كنت تفعل ذلك في المقام الأول منخفضة.

عرضي any s

من السهل التفكير في أن طرق التجاوز ستحصل على أنواع معلمات قاعدتها:

class Base {
  hasFilename(f: string) { return true; }
}
class Derived extends Base {
  // oops
  hasFilename(f) { return f.lentgh > 0; }
}

حاولنا كتابة هذه المعلمات تلقائيًا ولكننا واجهنا مشاكل. إذا تم وضع علامة صراحة على hasFilename override فقد نتمكن من حل هذه المشاكل بسهولة أكبر.

مقروئية

ليس من الواضح على الفور متى تتجاوز الطريقة طريقة الفئة الأساسية ومتى لا تكون كذلك. تبدو هذه مشكلة أصغر نظرًا لوجود حلول معقولة متاحة اليوم (التعليقات ، المصممون).

تجربة المحرر

يجب عليك كتابة أسماء الطريقة الأساسية يدويًا عند كتابة طرق تجاوز الصنف المشتقة ، وهو أمر مزعج. يمكننا إصلاح هذا بسهولة من خلال تقديم هذه الأسماء دائمًا في قائمة الإكمال (أعتقد أن لدينا بالفعل خطأ في هذا بالفعل) ، لذلك لا نحتاج حقًا إلى ميزة لغة لإصلاح هذه المشكلة.

غير مشاكل

ضع هذه الأشياء على أنها أشياء تنتمي إلى قضية منفصلة أو ببساطة لن تحدث:

  • مطابقة التوقيع التامة - هذا ليس شيئًا يجب خلطه بـ override ، وهو بالفعل يحتوي على دلالات غير مرغوب فيها في العديد من السيناريوهات الجيدة
  • تركيب غريب بشكل غير كافٍ (مثل implements.foo = ... ). لا حاجة لركوب الدراجة هنا

RyanCavanaugh أتفق مع كل ما سبق في هذا الترتيب أيضًا. كتعليق فقط ، لا أعتقد أن الكلمة الرئيسية override يجب أن تحل مشكلة "الفشل في التنفيذ". كما قلت أعلاه ، أعتقد أنه يجب حل هذه المشكلة عن طريق كلمة رئيسية مختلفة (على سبيل المثال implements ) كجزء من بطاقة مختلفة. (يجب أن يشير super. override $ # $ 3 $ # $)

RyanCavanaugh أنا أتفق مع جميع النقاط ولكني لا أرى أي إشارة إلى المشكلة ذات الصلة أيضًا بالحقول المهيأة المعلنة في النوع الأصلي دون تجاوز النوع (ثم فقدان التحقق من النوع). أعتقد أن الميزة لا ينبغي أن تقتصر على إعلانات الطريقة في لغة تكون فيها الطرق مجرد وظائف في حقول الكائن.

eggers حتى إذا كانت هناك حاجة في النهاية إلى كلمتين رئيسيتين ، فستكون الدلالات متشابهة جدًا وأعتقد أنه يجب مناقشة الميزات معًا.

يجب أن يعني التجاوز قيمة فائقة. طريقة

في C # ، يمكن (ويجب) استخدام التجاوز أيضًا للطرق المجردة (بدون. في Javaoverride هي سمة. بالطبع هما لغتان مختلفتان ولكن مع كلمات رئيسية متشابهة تتوقع سلوكيات مماثلة.

أتفق مع نقاط RyanCavanaugh ، على الرغم من أنني سأقول أن مشكلة "التجاوز العرضي" قد تكون أكثر شيوعًا مما يعتقد (خاصة عند التعامل مع تمديد فصل يمتد بالفعل إلى شيء آخر ، مثل مكون React الذي قد يكون بالفعل حدد طريقة componentWillMount في الامتداد الأول).

أعتقد ، على الرغم من ذلك ، كما قال eggers ، أن مشكلة "الفشل في التنفيذ" شيء مختلف تمامًا ، وسيكون من الغريب استخدام كلمة رئيسية override لطريقة غير موجودة في فئة الوالدين. أعلم أن هذه لغات مختلفة مع مشاكل مختلفة ، ولكن في C # override لا يتم استخدامها للواجهات. أود أن أقترح ، إذا تمكنا من الحصول على علامة --no-implicit-override ، فيجب أن تكون طرق الواجهة مسبوقة باسم الواجهة (والتي ستبدو مثل C # كثيرًا وبالتالي تبدو طبيعية أكثر).

شيء مثل

interface IBase {
    method1?(): void
}

class Base {
    method2() { return true; }
}

class Test extends Base implements IBase {
    IBase.method1() { }
    override method2() { return true; }
}

أعتقد أن RyanCavanaugh يسرد المشاكل الأساسية. أود أن أكمل ذلك بـ "ما هي الخلافات":

  • هل التصريح عن طريقة لمطابقة الواجهة يعتبر override ؟ علي سبيل المثال:
    interface IDrawable
    {
        draw?( centerPoint: Point ): void;
    }

    class Square implements IDrawable
    {
        draw( centerPoint: Point ): void; // is this an override?
    }
  • يعلن عن خاصية تطابق واجهة تعتبر override ؟
    interface IPoint2
    {
        x?: number;
        y?: number;
    }

    class Circle implements IPoint2
    {
        x: number; // is this an override?
        y: number; // is this an override?
        radius: number;
    }
  • هل هناك حاجة إلى نوع من الكلمات الرئيسية implements للتعامل مع الحالات المذكورة أعلاه بدلاً من الكلمة الرئيسية override ، والشكل الذي ستتخذه.

@ kevmeister68 شكرا لتوضيح الخلافات صراحة.

شيء واحد: عليك أن تجعل أعضاء الواجهة اختيارية لإظهار المشكلة حقًا ، مثل هذا سوف يشتكي المحول البرمجي من نوع الكتابة عندما لا يتم تنفيذ حقل أو طريقة ، مما يؤدي إلى حل مشكلة "فشل التنفيذ".

olmobrutall شكرا لذلك. لم أصرح مطلقًا عن طريقة اختيارية - هل يمكن أن تكون (جئت من خلفية C # ولا يوجد مثل هذا المفهوم)؟ لقد قمت بتحديث الأمثلة المذكورة أعلاه لتغيير متغيرات الأعضاء إلى اختيارية - هل هذا أفضل؟

@ kevmeister68 أيضًا طريقة draw بـ IDrawable :)

تضمين التغريدة

تركيب غريب بشكل غير كافٍ (على سبيل المثال ، implements.foo = ...). لا حاجة لركوب الدراجة هنا

شكرا لتعليمي تعبيرا جديدا . لا أرى الكثير من المشاكل في دلالات الميزة:

  • كلنا نريد أخطاء وقت الترجمة إذا لم تكن الطريقة موجودة
  • نريد جميعًا أخطاء وقت الترجمة التي لا يتطابق معها النوع.
  • نريد أيضًا كتابة المعلمات ضمنيًا إلى المعلمة الرئيسية ، مثل تعبيرات lambda.
  • نريد بعض المساعدة من IDE لكتابة الطريقة بشكل صحيح.

تعتمد معظم المشكلات على البنية والسلوكيات التي تنطوي عليها:

  • أعضاء اختياريون في الواجهة مقابل الأعضاء في الفئات الأساسية
  • الطرق مقابل الحقول (لا أعرف أين ستكون لامدا)

دعنا نقارن الشجرة بصيغ واعدة أكثر:

تجاوز / الأدوات

class Person{
    dateOfBirth: Date;

    abstract talk();
    walk(){ //...}
}

interface ICanFly{
    fly?();
    altitude?: number;
}


class SuperMan extends Person implements ICanFly {
     dateOfBirth = new Date(); //what goes here?

     override talk(){/*...*/}
     walk = () => {/* force 'this' to be captured*/}  //what goes here

     implements fly() {/*...*/}
     altitude = 1000; //what goes here?
}

مزايا:

  • إنه أمر طبيعي للمبرمجين الذين لديهم خلفية OO.
  • يبدو صحيحًا عند المقارنة بـ extends / implements .

سلبيات:

  • لا يوفر حلاً للمشكلة الميدانية ، override implements يبدو أنه صحيح. ربما super. ، لكن ماذا نفعل مع الواجهات؟
  • إذا تم تجاوز العمليات باستخدام lambda (مفيد في التقاط هذا) function() ، فستحدث نفس المشكلات.

تجاوز / InterfaceName.

class Person{
    dateOfBirth: Date;

    abstract talk();
    walk(){ //...}
}

interface ICanFly{
    fly?();
    altitude?: number;
}


class SuperMan extends Person implements ICanFly {
     dateOfBirth = new Date(); //what goes here?

     override talk(){/*...*/}
     walk = () => {/* force 'this' to be captured*/}  //what goes here

     ICanFly.fly() {/*...*/}
     ICanFly.altitude = 1000; //what goes here?
}

مزايا:

  • هذا طبيعي أيضًا للمبرمجين الذين لديهم خلفية OO.
  • يحل مشكلة المجال للواجهات ، ولكن ليس للفئات ، ولكن يمكننا استخدام super. لها.

سلبيات:

  • يمكن أن تكون أسماء الواجهة طويلة ، خاصةً مع الأدوية الجنيسة ، كما أن إظهار قائمة المنافسة التلقائية سيكون مشكلة أيضًا ، مما يتطلب بعض Alt + Space لتوضيحها.
  • يجعل الأمر يبدو أنه يمكنك تنفيذ عضوين بنفس الاسم من واجهات مختلفة بشكل مستقل. هذا يعمل في C # لكنه لن يعمل في Javascript.

this.

class Person{
    dateOfBirth: Date;

    abstract talk();
    walk(){ //...}
}

interface ICanFly{
    fly?();
    altitude?: number;
}


class SuperMan extends Person implements ICanFly {
     this.dateOfBirth = new Date();

     this.talk(){/*...*/}
     this.walk = () => {/* force 'this' to be captured*/} 

     this.fly() {/*...*/}
     this.altitude = 1000;
}

مزايا:

  • يحل الفئات والواجهات والأساليب والمجالات.
  • يستخدم (إساءة؟) مجرد كلمة رئيسية واحدة موجودة مسبقًا.
  • يبدو استخدام this. لتشغيل الإكمال التلقائي أمرًا طبيعيًا.
  • يوضح أن الكائن ما هو إلا مساحة اسم واحدة ، ولا يمكنك الحصول إلا على شيء واحد لكل اسم.

سلبيات:

  • يبدو وكأنه تعبير ، وليس تصريح ، مما يجعل من الصعب تحليله للعيون غير المدربة.

في حين أن الفشل في تنفيذ طريقة واجهة غير اختيارية سيؤدي إلى حدوث خطأ في المترجم ، إذا تغيرت الواجهة في وقت ما في المستقبل (دعنا نقول أنه تمت إزالة الطريقة) ، فلن يكون هناك تحذير لجميع الفئات التي طبقت هذه الطريقة. قد لا تكون هذه مشكلة كبيرة مثل المشكلة الاختيارية ولكن أعتقد أنه من العدل أن نقول إن نطاق هذا يتجاوزها.

ومع ذلك ، ما زلت أعتقد أن override شيء مختلف تمامًا ومن المحتمل أن يكون لديك كلمتان رئيسيتان بدلاً من واحدة من شأنه أن ينقل بشكل أفضل هدف البرنامج.

على سبيل المثال ، ضع في اعتبارك سيناريو يقوم فيه مطور _believes_ بتنفيذ طريقة واجهة عندما يقوم في الواقع بإلغاء الطريقة التي تم الإعلان عنها بالفعل في فئة أساسية. باستخدام كلمتين رئيسيتين ، يمكن للمجمع أن يمنع هذا النوع من الخطأ ويحدث خطأ في نغمة method already implemented in a base class .

interface IDelegate {
    execute?() : void;
}

class Base implements IDelegate {
    implement public execute() : void { /// fine, this is correctly implementing execute
    }
}

class Derived extends Base {
    implement public execute() : void { 
/// ERROR: `method "execute():void" already implemented in a base class`
    }
}

لا أعرف ما إذا كان هذا مناسبًا في JS ، ولكن من المفترض أن تكون حقول الفصل خاصة في OO ، لذلك لا أعتقد أننا بحاجة إلى تجاوز الحقول بشكل صريح لأن حقيقة أنها خاصة بالفعل تمنعنا من تجاوزها تمامًا .

طرق المثيل هي حقول على الرغم من ذلك ، ومن ما أجمعه يستخدمها الكثير من الأشخاص بدلاً من أساليب النموذج الأولي. عن ذلك،

class Person{
    walk(){ //...}
}
class SuperMan extends Person  {
     walk = () => {/* force 'this' to be captured*/}
}

هو خطأ في المترجم ، لأن walk هو أسلوب نموذج أولي في Person وطريقة مثيل في SuperMan .

على أي حال ، لست متأكدًا من أن override سيكون مناسبًا هنا ، لأنه حسنًا ، نحن لا نتجاوز الحقول. مرة أخرى ، سيبدو مثل C # ، لكنني أفضل استخدام الكلمة الرئيسية new بدلاً من override هنا. لأنه ليس نفس الشيء مثل تجاوز الطريقة (يمكنني استدعاء super.myMethod في التجاوز ، ليس هنا).

سيكون الحل المفضل عندئذٍ شيئًا مثل (بافتراض أننا في وضع تجاوز صارم):

class Person{
    dateOfBirth: Date;
    talk() { }
    walk = () => { }
}

interface ICanFly {
    fly?();
    altitude?: number;
}

class SuperMan extends Person implements ICanFly {
     new dateOfBirth = new Date();
     override talk() { }
     new walk = () => { }

     implements fly() {/*...*/}
     implements altitude = 1000;
}

قلقي الرئيسي هو حول الواجهات بالرغم من ذلك. لا يجب علينا كتابة implements لأعضاء الواجهة غير الاختياريين ، لأننا قد تم اكتشافنا بالفعل من قبل المترجم. بعد ذلك ، إذا قمنا بذلك ، فسيكون هناك التباس بين ما هو من واجهة وما هو ليس كذلك ، حيث لن يكون كل أعضاء الواجهات مسبوقًا بـ implements . وهذه الكلمة مليئة بالفم. لست مستعدًا لكتابة شيء مثل هذا:

class C extends React.Component {
    implements componentWillMount() { }
    implements componentDidMount() { }
    implements componentWillReceiveProps(props) { }
    /// ... and the list goes on
}

اقترحت سابقًا إضافة اسم الواجهة ، لكن olmobrutall أظهر لي أنها كانت فكرة أسوأ.

على أي حال ، أنا مقتنع تمامًا أننا سنحتاج إلى 3 كلمات رئيسية مختلفة لمعالجة هذا الأمر بشكل صحيح.

لا أعتقد أيضًا أنه من الضروري كتابة التجاوزات بشكل ضمني لأن المترجم سيمنعنا بالفعل من كتابة شيء غير متوافق ، خاصةً لأنه من الواضح أنه من الصعب القيام به بشكل صحيح.

أليس new ، override و implement الكثير من الكلمات الرئيسية الزائدة عن دلالات JS بشكل أساسي؟ حتى لو كانت هناك أشياء مختلفة في C # ، فلا يوجد فرق افتراضي في JS / TS.

إنه أيضًا غامض تمامًا:

  • لماذا الحقول هي new للفصول بينما implements للواجهات؟
  • إذا كنت تقوم بتغيير طريقة من فئة أساسية تم تنفيذها باستخدام واجهة ، فما الذي يجب عليك استخدامه؟ أستطيع أن أرى أربعة احتمالات: override ، implements ، أي من الاثنين أو كليهما في نفس الوقت؟

فكر أيضًا في هذا:

class Animal {
}

class Human extends Animal {
}

class Habitat {
    owner: Animal;
}

class House extends Habitat {
    owner = new Human();
}

var house = new House();
house.owner = new Dog(); //Should this be allowed??  

السؤال هو:

هل يقوم owner = new Human(); بإعادة تعريف الحقل بنوع جديد (لكن متوافق) ، أو يقوم فقط بتعيين قيمة.

أعتقد بشكل عام أنك قمت بإعادة تعريف الحقل إلا إذا كنت تستخدم الكلمة الأساسية السحرية. أفضّل الآن مقابل this. .

class House extends Habitat {
    this.owner = new Human(); //just setting a value, the type is still Animal
}

olmobrutall قد يكون هذا بالفعل عبارة عن عدد كبير جدًا من الكلمات الرئيسية ، ولكن الثلاثة كلها أشياء _ مختلفة_ بالفعل.

  • سيتم تطبيق override على الطرق (المحددة في النموذج الأولي) ، مما يعني أنه لا _ يمحو الطريقة الأصلية ، والتي لا يزال من الممكن الوصول إليها خلف super .
  • سيتم تطبيق new على الحقول ، وهذا يعني أن الحقل الأصلي قد تم محوه بواسطة الحقل الجديد.
  • يمكن تطبيق implements على أي شيء من واجهة ، وهذا يعني أنه لا يمحو أو يستبدل أي شيء موجود بالفعل في فئة رئيسية ، لأن الواجهة "غير موجودة".

إذا كنت تقوم بتغيير طريقة من فئة أساسية تم تنفيذها باستخدام واجهة ، فما الذي يجب عليك استخدامه؟ أستطيع أن أرى أربعة احتمالات: تجاوز ، أدوات ، أي من الاثنين أو كليهما في نفس الوقت؟

يبدو الأمر منطقيًا جدًا بالنسبة لي أنه سيكون override لأن هذا ما تفعله هنا بشكل فعال. يعني implements أنك تنفذ شيئًا من الواجهة غير موجود بخلاف ذلك. بمعنى ما ، ستكون الأولوية override و new أكثر من implements .

وحول نموذج تجاوز مجالك ، لا ينبغي أن يتغير شيء حول كيفية عمله اليوم. لست متأكدًا مما يحدث هنا ، ولكن مهما كان الأمر لا ينبغي أن يتغير (أو ربما يجب أن يتغير ، لكن هذا ليس ما نناقشه هنا).

أشعر أن override سيكون مناسبًا لكل من الأساليب والخصائص الأساسية ، بما في ذلك الأساليب المجردة (الموضحة أدناه).

new هي فكرة مثيرة للاهتمام من حيث المفهوم ، ولكن قد يتم الخلط بين هذه الكلمة الأساسية المحددة وإنشاء مثيل للفئة ، لذلك قد تعطي انطباعًا خاطئًا أنها لن تعمل إلا للفئات ، على الرغم من حقيقة أن الخصائص يمكن أن يكون لها واجهة بدائية واتحاد أو حتى أنواع الوظائف. ربما يمكن أن تعمل كلمة رئيسية مختلفة مثل reassign بشكل أفضل هناك ، ولكن هناك مشكلة في أنها قد تعطي فكرة خاطئة في حالة إعادة التصريح عن القيمة فقط ولكن لم يتم تعيينها فعليًا مع أي شيء في الفئة المشتقة ( new قد تواجه هذه المشكلة أيضًا). أعتقد أن redefine مثير أيضًا ، ولكن قد يؤدي إلى توقع خاطئ بأن الخاصية "المعاد تعريفها" يمكن أن يكون لها أيضًا نوع مختلف عن الخاصية الأساسية ، لذلك لست متأكدًا .. (_تعديل: في الواقع لقد راجعت ويمكن أن يكون لها نوع مختلف طالما أن النوع الجديد هو نوع فرعي من النوع الأساسي ، لذلك قد لا يكون هذا بهذا السوء_).

يبدو أن implement (أفضل صيغة الفعل هذه للتوافق مع override ) تعمل مع الواجهات. أعتقد أنه يمكن أن يعمل أيضًا من الناحية الفنية مع الأساليب الأساسية المجردة أيضًا ، ولكن استخدام override قد يبدو أكثر اتساقًا ، على الرغم من الدلالات المختلفة قليلاً. سبب آخر هو أنه سيكون من غير الملائم تغيير طريقة من مجرد إلى غير مجردة ، حيث يحتاج المرء والذهاب إلى جميع الفئات المشتقة وتغيير override إلى implement .

يمكن أن تكون هناك أفكار أفضل ، ولكن هذا كل ما لدي في هذه المرحلة ..

لقد اقترحت الكلمة الرئيسية new لأنها الكلمة الرئيسية التي يستخدمها C # لهذه الميزة بالضبط ، لكنني أوافق على أن هذه ليست الأفضل. implement هو بالفعل خيار أفضل من implements . لا أعتقد أننا يجب أن نستخدمها للطرق المجردة لأنها جزء من فئة أساسية وليست واجهة. override + new -> الفئة الأساسية ، implement -> الواجهة. يبدو ذلك أكثر وضوحا.

تضمين التغريدة

حول تجاوز النموذج الأولي مع حقل مثيل

لقد تحققت وأنت على حق:

class Person{
    walk(){ //...}
}
class SuperMan extends Person  {
     walk = () => {/* force 'this' to be captured*/}
}

فشل مع وجود خطأ في المترجم: Class 'Person' defines instance member function 'walk', but extended class 'SuperMan' defines it as instance member property.

لا أرى حقًا سبب الفشل في هذه الحالة ، نظرًا لاستيفاء عقد نوع الوالدين ويمكنك كتابة هذا على أي حال:

var p = new SuperMan();
p.walk = () => { };

او حتى

class SuperMan extends Person {
    constructor() {
        super();
        this.walk = () => { };
    }
}

حول override

ينطبق التخطي على الطرق (المحددة في النموذج الأولي) ، مما يعني أنه لا يمحو الطريقة الأصلية ، والتي لا يزال من الممكن الوصول إليها خلف سوبر.

هذا في الواقع حجة. يوجد على الأقل بعض الفروق الملحوظة بين override و implements ... لكن ليس تمامًا.

تم تنفيذ كل من override و implements في prototype ، القدرة على استخدام super مستقلة ، لأنك تطلب super.walk() وليس super() فقط

class SuperMan extends Person implements ICanFly {
     new dateOfBirth = new Date();
     override talk() { } //goes to prototype
     new walk = () => { }

     implements fly() {/*...*/}  //also goes to the prototype
     implements altitude = 1000;
}

والقدرة على استخدام super.walk() تعمل أيضًا إذا قمت بتعيينها للمثيل

class SuperMan extends Person {
    constructor() {
        super();
        this.walk = () => { super.walk(); };
    }

    //or with the this. syntax
    this.walk = () => { super.walk(); };
}

هناك بالفعل طريقة واضحة للتمييز بين حقول prototype من حقول المثيل وهي الرمز المميز = .

class SuperMan extends Person implements ICanFly {
     this.dateOfBirth = new Date(); //instance
     this.talk() { } //prototype
     this.walk = () => { } //instance

     this.fly() {/*...*/}  //prototype
     this.altitude = 1000; //instance
}

باستخدام بنية this. ، يتم حل المشكلة بطريقة أكثر أناقة:

  • يعني this. التحقق من النوع الخاص بي (الفئات الأساسية والواجهات المطبقة).
  • يعني = التخصيص للمثيل بدلاً من النموذج الأولي.

حول New

سيتم تطبيق new على الحقول ، وهذا يعني أن الحقل الأصلي قد تم محوه بواسطة الحقل الجديد.

ضع في اعتبارك أنه لا يمكنك تغيير الحقل أو الطريقة بنوع مختلف لأنك ستكسر نظام النوع. في C # أو Java عندما تفعل new ، سيتم استخدام الطريقة الجديدة فقط على المكالمات المرسلة بشكل ثابت إلى النوع الجديد. في Javascript ، ستكون جميع المكالمات ديناميكية ، وإذا قمت بتغيير النوع ، فمن المحتمل أن تنكسر الشفرة في وقت التشغيل (يمكن السماح بإجبار النوع لأغراض عملية ، ولكن ليس للتوسيع).

class Person {
    walk() { }

    run() {
        this.walk();
        this.walk();
        this.walk();
    }
}

class SuperMan extends Person {
    new walk(destination: string) { } //Even if you write `new` this code will break
}

لذلك يجب أن تكون الكلمة الرئيسية أكثر من new assign ، لأن هذا هو الشيء الوحيد الآمن من النوع الذي يمكنك القيام به:

class Person{
    dateOfBirth: Date;
}

class SuperMan extends Person implements ICanFly {
     assign dateOfBirth = new Date();
}

لاحظ أن reassign ليس له معنى ، لأن الشخص يعلن فقط عن الحقل ولكن لا يقوم بتعيين أي قيمة عليه.

كتابة assign تبدو زائدة عن الحاجة ، لكن من الواضح أننا نقوم بالتخصيص لأن لدينا = في الجانب الآخر.

مرة أخرى ، يحل بناء الجملة this. المشكلة بأمان:

class Person{
    dateOfBirth: Date;
}

class SuperMan extends Person implements ICanFly {
     this.dateOfBirth = new Date();
}

لست متأكدًا من كيفية تطبيق هذا على الحقول بطريقة منطقية.

نحن نتحدث عن إضافة كلمة رئيسية قابلة للتطبيق فقط في نص الفصل ، ومع ذلك يمكن إعادة تعيين الحقول في _أي نقطة_ أثناء عمر المثيلات.

تتيح لك الكتابة النصية بالفعل تهيئة الحقول في نص الفصل الدراسي:

class Person {
    dateOfBirth : new Date();
}

كما يسمح لك بإعادة تهيئة الحقول المعلنة في الفصل الأصلي ، لكن الوضع سيء. بالطبع يمكنك كتابة الاسم بشكل خاطئ (مشكلة مماثلة لـ override ) ولكن يتم مسح النوع أيضًا. هذا يعني ذاك:

class Person {
    fullName: { firstName: string; lastName?: string };
}

class SuperMan extends Person {
    fullName = { firstName: "Clark" };

    bla() {
        this.fullName.lastName; //Error
    }
}

فشل هذا الرمز مع: الخاصية 'lastName' غير موجودة في النوع '{firstName: string؛

نحن نتحدث عن إضافة كلمة رئيسية قابلة للتطبيق فقط في نص الفصل ، ومع ذلك يمكن إعادة تعيين الحقول في أي وقت خلال عمر المثيلات.

أيضا الطرق:

class Person {
    talk() { }
}

class SuperMan extends Person {

    talk() { }

    changeMe(){
        this.talk = () => { };      
    }

    changeMyPrototype() {
        SuperMan.prototype.talk = () => { };
    }
}

kungfusheep أوافق ، يمكن إعادة تعيين الحقول ، ولكن أيضًا الأساليب في النموذج الأولي.
السبب الرئيسي وراء احتياجنا إلى كلمة رئيسية لحقول "تجاوز" هو أن طرق المثيل _ هي الحقول ويتم استخدامها على نطاق واسع بدلاً من طرق النموذج الأولي المناسبة لأنها lambdas وبالتالي ربط السياق تلقائيًا.

تضمين التغريدة

حول تجاوز الأساليب مع الحقول

إنه محظور وأعتقد أنه لسبب وجيه (إنه ليس نفس الشيء لذا لا ينبغي أن يكون متوافقًا) ، لكنك محق في أنه من الممكن القيام بذلك عند التلاعب مباشرة بمثال الفصل. لا أعرف بالضبط سبب السماح بذلك ، لكننا لسنا هنا لتغيير كيفية عمل اللغة ، لذلك أعتقد أنه لا ينبغي لنا التدخل هنا.

حول الواجهات

تصف واجهات الفئة جانب المثيل للفئة ، مما يعني الحقول بالإضافة إلى الطرق من النموذج الأولي. لا أعتقد حتى أن هناك فرقًا بين طرق المثيل والنموذج الأولي في الواجهة ، أعتقد أنه يمكنك وصف طريقة ما كواحدة أو أخرى ، وتنفيذها كواحدة أو أخرى. لذلك يمكن (ويجب) تطبيق implement أيضًا على الحقول.

عن الجديد

يجب أن يكون لدى new نفس الدلالات مثل override ، لذلك من الواضح أنه لا يمكنك تغيير النوع إلى شيء غير متوافق. أعني ، مرة أخرى ، نحن لا نغير طريقة عمل اللغة. أردت فقط كلمة رئيسية منفصلة لأنها ليست من نفس النوع من إعادة التعريف. لكنني أوافق ، كما أوضحت لي ، أنه يمكننا بالفعل التمييز بين الحقول والطرق باستخدام = ، لذلك ربما يمكن استخدام نفس الكلمة الأساسية / بناء الجملة بدون غموض.

حول هذا.

ومع ذلك ، لا ينبغي أن تكون صيغة تجاوز الحقل / الطريقة الموحدة this.method() أو this.field لأنها تشبه إلى حد كبير شيئًا يمكن أن يكون جافا سكريبت صالحًا ، في حين أنه ليس كذلك. ستكون إضافة كلمة رئيسية قبل العضو أكثر وضوحًا تجاه حقيقة أن هذا هو بالفعل شيء منسوخة.
ومع ذلك ، فإن this فكرة جيدة ، لذا ربما يمكننا حذف النقطة وكتابة شيء أقل غموضًا مثل

class Superman {
    this walk() { }
}

لكن مع ذلك ، يبدو الأمر غريبًا. ومزجها مع implement سيبدو قليلاً في غير محله (لأننا على ما يرام مع حقيقة أن بناء الجملة هذا this لن يتم استخدامه مع الواجهة؟) ، وأنا أحب implement / override ثنائي. لا يزال معدّل override في الحقول يزعجني ، ولكن ربما يكون من الأفضل وجود كلمة رئيسية ثالثة غامضة new . نظرًا لأن التخصيص ( = ) يجعل الفرق بين الطريقة والحقل واضحًا ، فسيكون ذلك جيدًا بواسطتي الآن (على عكس ما كنت أقوله قبل بضع ساعات).

أوافق على أنه يمكن إعادة تعيين الحقول ، لكن كذلك تفعل الطرق في النموذج الأولي.

نعم ، يعد تعديل النموذج الأولي مباشرةً ، في TypeScript ، أمرًا شائعًا جدًا في العوالم التي يتم العبث بها تحت الغطاء. إنه ليس شائعًا في أي مكان مثل إعادة تعيين خاصية على مثيل لشيء ما. نادرًا ما نستخدم الحقول كوظائف ، أكثر من 150 ألف + سطر كود ، لذلك أعتقد أن استخدامها على نطاق واسع ربما يكون ذاتيًا.

أتفق مع معظم نقاطك الأخرى ، لكنني لا أبيع (وبصوتها ، أنت أيضًا ...) حول استخدام الكلمة الرئيسية new في هذا السياق.

نادرًا ما نستخدم الحقول كوظائف ، أكثر من 150 ألف + سطر كود ، لذلك أعتقد أن استخدامها على نطاق واسع ربما يكون ذاتيًا.

أنا لا أفعل ذلك أيضًا ، لكن لا بد لي من اللجوء إلى مصمم الديكور @autobind لربط this بشكل صحيح في أساليبي ، وهو أمر مرهق أكثر من مجرد كتابة lambda بدلاً من ذلك. وطالما أنك لا تستخدم الميراث ، فإن استخدام الحقل يعمل كما تتوقع. كان لدي انطباع بأنه ، على الأقل في عالم JS / React ، كان معظم الأشخاص الذين يستخدمون فئات ES6 يستخدمون وظائف الأسهم كطرق.

ويمكن تنفيذ طرق الواجهة إما بطريقة مناسبة أو بطريقة مثيل ، والتي لا تساعد في توضيح الفرق.

أعتقد أن الحقول لديها نفس المشاكل مثل الأساليب بقدر ما يتعلق الأمر بالتجاوزات.

class A {
    firstName: string;
    get name() {
        return this.firstName;
    }
}

class B extends A {
    firstname = "Joe" // oops
}

الحل المحتمل هنا هو تعيين الحقل في المنشئ

class B extends A {
    constructor() {
        this.firstName = "Joe"; // can't go wrong
    }
}

لكن هذا لا ينطبق على الواجهات. لهذا السبب أنا (والآخرون هنا) أعتقد أننا بحاجة إلى شيء للتحقق من الوجود المسبق لمجال ما عند إعلانه مباشرة في جسم الفصل. وهو ليس override . الآن بعد أن وضعت بعض الأفكار هنا ، أعتقد أن المشكلة مع الحقول هي بالضبط نفس المشكلة مع أعضاء الواجهة. أود أن أقول إننا بحاجة إلى كلمة رئيسية override للطرق التي تم تنفيذها بالفعل في فئة أصل (طرق النماذج الأولية ، وربما طرق المثيل أيضًا) ، وأخرى للأشياء التي تم تعريفها ولكن بدون تنفيذ (غير دالة وشملت الحقول).

سيكون اقتراحي الجديد ، وباستخدام كلمة رئيسية مبدئية member (ربما لا تكون الخيار الأفضل):

interface IBase {
    interfaceField?: string;
    interfaceMethod(): void
}

abstract class Base {
    baseField: number;
    baseMethod() { }
    baseLambda: () => { };
    abstract baseAbstractMethod();
}

class Derived extends Base implements IBase {
    member interfaceField = "Hello";
    member interfaceMethod() { }
    member baseField = 2;
    override baseMethod() { }
    override baseLambda = () => { };
    member baseAbstractMethod() { }
}

لاحظ أنني سأختار الكلمة الرئيسية member لطريقة مجردة لأنني قلت إن override سيكون للأشياء ذات التنفيذ ، مما يشير إلى أننا نستبدل سلوكًا موجودًا. قد تحتاج طرق المثيل إلى استخدام override بحكم كونها نوعًا خاصًا من الحقول (والتي تم التعرف عليها بالفعل من قبل المترجم حيث يمكن تنفيذ تواقيع الطريقة باستخدامها).

أعتقد أن Typescript يجب أن تكون نسخة مُدققة في وقت الترجمة من JavaScript ، ومن خلال تعلم TS ، يجب أن تتعلم JS أيضًا ، تمامًا كما تعلم C # تحصل على حدس جيد لـ CLR.

الميراث النموذجي هو مفهوم رائع لـ JS ، وهو مفهوم مركزي تمامًا ، ويجب ألا يحاول TS إخفاءه بمفاهيم من لغات أخرى لا معنى لها هنا.

الميراث النموذجي لا يحدث أي فرق إذا كنت ترث الأساليب أو الحقول.

علي سبيل المثال:

class Rectangle {
       x: number;
       y: number;
       color: string;
}

Rectangle.prototype.color = "black";

نحن هنا نضع حقلاً بسيطًا في كائن النموذج الأولي ، لذلك ستكون جميع المستطيلات سوداء بشكل افتراضي دون الحاجة إلى وجود حقل مثيل لها.

class BoundingBox {
      override color = "transparent"; // or should be member? 
}

كما أن الكلمة الأساسية member تجعل الأعضاء الآخرين في الفصل يشعرون بالغيرة.

ما نحتاجه هو بناء جملة يسمح في سياق إعلان الفئة بنفس نوع السلوك (فحص وقت التجميع / الإكمال التلقائي / إعادة التسمية) الذي لدينا بالفعل في الكائن الحرفي أو تعبيرات الأعضاء.

ربما يكون البديل الأفضل لـ this. هو . فقط:

class Derived {
   .interfaceField = "hello";
   .interfaceMethod() {}
   .baseField = 2;
   .baseMethod() {}
   .baseLambda = () => {};
   .baseAbstractMethod(){};

   someNewMethod(){}
   someNewField = 3;
}

في الختام ، لا أعتقد أن نظام الأنواع يتتبع القيم التي تأتي من النموذج الأولي وأيها من المثيل ، لأن هذه مشكلة صعبة بمجرد أن تتمكن من الوصول إلى النموذج الأولي بشكل إلزامي ، ولأنه لن يقوم بإصلاح أي مشكلة أثناء الحد من تعبير JS.

لذلك لا يوجد فرق بين حقل الوظيفة ، وحقل دالة السهم وطريقة ، ولا في نظام النوع ، ولا في الكود المُنشأ (إذا لم يتم استخدام this ).

مرحبًا يا رفاق ، هذه أشياء مثيرة للاهتمام ، لكنها جميلة جدًا من حيث override على وجه التحديد. سأكون سعيدًا لوجود بعض المشكلات الجديدة للمناقشة بشأن هذا النوع من الأشياء ما لم نتمكن من ربط هذه التعليقات مرة أخرى بالاقتراح الأصلي بشكل ملموس أكثر.

الكثير مما تقوله هنا ليس خاصًا بـ TypeScript أيضًا ، لذا قد يكون بدء مناقشة ESDiscuss مناسبًا أيضًا. بالتأكيد لقد فكروا في هذا النوع من الأشياء بنفس القدر (من حيث ما يحدث في النموذج الأولي مقابل المثال).

تضمين التغريدة
تقوم فصول ES6 بالفعل بإخفاء نماذج النماذج الأولية ، وكما قال kungfusheep ، فإن العبث بها مباشرة ليس بالأمر الطبيعي.

لذلك لا يوجد فرق بين حقل الوظيفة ، وحقل دالة السهم وطريقة ، ولا في نظام النوع ، ولا في الكود المُنشأ (إذا لم يتم استخدام هذا).

حسنًا ، في الكود الذي تم إنشاؤه ، يتم وضع طرق الفئة في النموذج الأولي وكل شيء آخر (غير مسبوق بـ static ) في المثال ، مما يحدث فرقًا في الوراثة.

على أي حال ، أوافق الآن على أنه ليس علينا أن يكون لدينا بناء جملة منفصل لكلا النوعين من الطريقتين ، ولكن إذا أردنا استخدام الكلمة الرئيسية override ، فيجب أن تقتصر على الطرق وعلينا أن نجد شيء آخر للباقي. قد يكون امتلاك كلمة رئيسية فريدة لكل شيء أمرًا رائعًا ، لكن يجب أن يكون واضحًا جدًا بشأن معناها. override واضح ، لكن للأساليب فقط. لقد حددت بناء الجملة ، مثل this. لا يزال قريبًا جدًا من JS الحالي بالنسبة لي.

تضمين التغريدة

إنه متقلب من الظل من حيث override

مشاكلي مع التجاوز هي أن هذا ليس عامًا بما يكفي لطرق وحقول الواجهة. أرى ثلاثة خيارات:

  • استخدم override فقط لطرق الفئات الأساسية.
  • استخدم override لطرق وحقول الواجهة أيضًا
  • ضع في اعتبارك الصيغ البديلة ( member ، implement ، this. ، . ، إلخ ...)
  • لا تفعل شيئا (سيكون ذلك حزينا)

أعتقد أنه ينبغي اتخاذ هذا القرار هنا.

الكثير مما تقوله ليس خاصًا في الحقيقة المطبعية

إطلاقا! نحن جميعًا سعداء بالحالة الحالية للترجمة ، ولكن ليس في فحص النوع / الأدوات.

مهما كانت الكلمة الأساسية ، فستكون مطبوعة فقط (تمامًا مثل الملخص)

تضمين التغريدة

أنت محق بشأن الكود الذي تم إنشاؤه ، بالطبع. كانت وجهة نظري أنه يمكنك كتابة شيء مثل هذا:

class Person {
    name: string = "John"; 

    saySomething() {
        return "Hi " + this.name;
    }
}

نعلن فئة ، name سيذهب إلى المثيل بينما saySomething إلى النموذج الأولي. لا يزال بإمكاني كتابة هذا:

Person.prototype.name = "Unknown"; 

لأن نوع Person.prototype هو الشخص كله. لا يتتبع الطباعي ما يحدث في نظام الكتابة الخاص به من أجل البساطة.

قد يكون امتلاك كلمة رئيسية فريدة لكل شيء أمرًا رائعًا ، لكن يجب أن يكون واضحًا جدًا بشأن معناها.

ما أعتقد أنه أكثر أهمية ويمكن أن نتفق جميعًا هو الدلالات:

يتحقق XXX المعدل من أن العضو قد تم التصريح عنه بالفعل في الفئة الأساسية أو واجهات مطبقة ، وعادة ما تستخدم لتجاوز الوظائف من الفئة الأساسية.

نظرًا لأن . أو this. يبدو غريبًا جدًا ، و member زائدة عن الحاجة ، أعتقد أن الخيار الأفضل على الأرجح هو إساءة استخدام override لجميع الحالات . يعد تعيين الحقول أو تنفيذ طرق الواجهة ميزة جانبية على أي حال.

هناك العديد من السوابق:

  • في C # لم يعد static class فئة من الكائنات بعد الآن.
  • لا يوجد شيء _ ثابت_ حول static الحقول.
  • طرق virtual تمامًا _concrete_ (يستخدم VB Overrideable ).

سيبدو مثل هذا:

class Person{
    dateOfBirth: Date;

    abstract talk();
    walk(){ //...}
}

interface ICanFly{
    fly?();
    altitude?: number;
}


class SuperMan extends Person implements ICanFly {
     override dateOfBirth = new Date();

     override talk(){/*...*/}
     override walk = () => {/* force 'this' to be captured*/} 

     override  fly() {/*...*/}
     override altitude = 1000; 
}

هل يمكننا تسوية ذلك؟

أفضل أن أرى كلمة رئيسية منفصلة implement لأي شيء يأتي من الواجهات (مع الأولوية override إذا كانت في كليهما) ، لأنها ما هي عليه: تطبيق ، وليس تجاوز.
بخلاف ذلك ، أوافق على أنه قد يكون من الأفضل إساءة استخدام override لكل شيء من فئة الوالدين.

ولكن لا يوجد فرق دلالي بين implement و override .

سيحصل كلاهما على رسائل إكمال تلقائي / خطأ / ترشيح مماثلة لـ JS ... إنه مجرد اختلاف فلسفي.

من الجدير حقًا شرح كلمتين رئيسيتين وأن المترجم المتحذلق يخبرك: Error you should use 'override' instead of 'implement' .

ماذا عن هذه الحالة:

interface IComparable {
     compare(): number;
} 

class BaseClass implements IComparable {
    implement compare(); 
}

class ChildClass extends BaseClass implements IComparable { //again 
     override compare(); // or implements... 
}

السؤال ... من يهتم ؟.

هناك أيضًا مشكلة implement / implements .

دعنا فقط نسيء استخدام override . مفهوم واحد كلمة رئيسية واحدة ، هذا هو الشيء المهم.

حسنًا ، لقد اقترحت بالفعل أن يكون لـ override الأولوية على implement ، لكن ربما لم أكن واضحًا بما يكفي.
ما زلت لا أعتقد أن هذا هو نفس الشيء. مثال آخر: بالنسبة لعضو الواجهة الإلزامي ، فإن الفشل في التنفيذ هو خطأ في المترجم بينما الفشل في التجاوز ليس مشكلة (ما لم يكن العضو الأساسي مجرداً أي ...).

أنا فقط لا أعتقد أن override على شيء لم يتم التصريح به في فئة الوالدين سيكون له أي معنى. لكن ربما أنا الوحيد الذي يريد التمييز. على أي حال ، بغض النظر عن الكلمة الرئيسية التي ننتهي باستخدامها لهذا الغرض ، آمل فقط أن نتمكن من تسوية شيء ما وتوفير وضع صارم لفرض استخدامه.

حسنًا ، لقد اقترحت بالفعل أن الإلغاء يجب أن يكون له الأولوية على التنفيذ ، لكن ربما لم أكن واضحًا بما فيه الكفاية.

بالتأكيد ، أردت فقط أن أقول إن هناك أربع حالات. الفئة الأساسية ، والواجهة ، والواجهة في الفئة الأساسية ، وإعادة تنفيذ الواجهة في BaseClass.

تضمين التغريدة

أنا فقط لا أعتقد أن تجاوز شيء لم يتم التصريح به في فصل أولياء الأمور سيكون له أي معنى. لكن ربما أنا الوحيد الذي يريد التمييز .

أنت لست. إنهما شيئان مختلفان اختلافًا جوهريًا وهناك فائدة من وجود هذا الفصل على مستوى اللغة.

أعتقد أنه إذا كان سيتم دعم الواجهات في هذه الوظيفة الجديدة ، فلا يمكن أن تكون بعض الحلول نصف المخبوزة التي تم تثبيتها على override . للسبب نفسه ، يوجد extends ككيان منفصل عن implements على مستوى الفصل ، يجب إقران override بشيء على غرار implement بالترتيب لإصلاح هذه المشكلة. إنها _ استبدال الوظيفة_ مقابل _ تحديد الوظيفة_.

تضمين التغريدة

من الجدير حقًا شرح كلمتين رئيسيتين وأن المترجم المتحذلق يخبرك: خطأ يجب عليك استخدام "تجاوز" بدلاً من "تنفيذ".

أشعر أنني قمت بهذا التمييز حوالي 4 مرات الآن ، لكن هذا ليس متحذلقًا ؛ إنها حقًا معلومات مهمة!

النظر في المثال الخاص بك

interface IComparable {
     compare(): number;
} 

class BaseClass implements IComparable {
    implement compare(); 
}

class ChildClass extends BaseClass implements IComparable { //again 
     override compare(); // or implements... 
}

"أو أدوات ..." هو افتراض غير صحيح تمامًا في هذه الحالة. فقط لأنك أخبرت المترجم أنك تريد تنفيذ نفس الواجهة مثل الفئة الأساسية التي تقوم بتوسيعها لا يغير حقيقة أنك قمت بالفعل بتطبيق compare .
إذا كنت ستكتب implement في ChildClass بدلاً من override ، فيجب أن تكون شاكراً لأن المترجم سيخبرك بعد ذلك بافتراضك الخاطئ ، لأنه من الأمور الكبيرة التي كنت على وشك مسحها دون قصد من طريقة تم تنفيذها مسبقًا!

في الأكواد البرمجية التي أكون مسؤولاً عنها ، ستكون هذه مشكلة كبيرة بلا شك ؛ لذلك أرحب بأي ميزة للمترجم يمكن أن تمنع أي أخطاء مطور من هذا القبيل!

تضمين التغريدة

إذا كنت ستكتب تطبيقًا في ChildClass بدلاً من التجاوز ، فيجب أن تكون شاكراً لأن المترجم سيخبرك بعد ذلك بافتراضك الخاطئ ، لأنه من الأمور الكبيرة التي كنت على وشك التخلص منها دون قصد من طريقة تم تنفيذها مسبقًا!

إذا كان تجاوز طريقة تم تنفيذها بالفعل قرارًا مهمًا ، فيجب استخدام implement للطرق المجردة أيضًا. إذا قمت بتغيير طريقة من abstract إلى virtual أو بالطريقة الأخرى ، يجب عليك التحقق من (وتغيير) جميع الإصدارات المنفذة للنظر في استدعاء super أو إزالة الطريقة فقط.

من الناحية العملية ، لم تكن هذه مشكلة في C #.

أوافق ، override للطرق المنفذة ، و implements للطرق غير المنفذة (الملخص / الواجهة).

يمكن استخدامه للملخص ، نعم.

في C # ، لم يكن هناك سيناريو الواجهة "الاختيارية" ، لذلك ربما لم يتم اعتباره مشكلة كبيرة.

لكن وجهة نظري هي أننا في C override قمنا بتطبيق طرق غير مطبقة ولم أسمع أبدًا عن أي شخص يشتكي.

لا أعتقد أنها حقًا مشكلة تستحق جعل شرح الميزة أكثر صعوبة مرتين على الأقل.

حسنًا ، السبب الوحيد وراء حاجتنا إلى كلمة رئيسية implement هو أن أعضاء الواجهة يمكن أن يكونوا اختياريين ، في حين أنه غير ممكن في C # (وربما في معظم لغات OO أيضًا). لا توجد كلمة رئيسية لذلك هناك لأنه لا يمكنك _not_ تنفيذ واجهة بالكامل ، لذلك لا توجد مشكلة. لا تبني حجتك على "الأمر نفسه في C #" كثيرًا ، لأن لدينا إشكاليات مختلفة هنا.

يستخدم override لطرق _base class_ ، سواء كانت مجردة أم لا. أفترض أنه يجب علينا نفس الأمر ( override بدلاً من implement للطرق المجردة) هنا لأنني أعتقد أن التمييز يجب أن يكون على أصل الطريقة (الفئة أو الواجهة) بدلاً من الوجود من التنفيذ. وفي المستقبل ، إذا قررت إعطاء تطبيق افتراضي لطريقتك المجردة ، فلن تضطر إلى المرور عبر الكود الخاص بك (أو ما هو أسوأ ، إلى كود الآخرين الذين يستخدمون فصلك) لاستبدال الكلمات الرئيسية.

سؤالي الرئيسي الآن هو ، هل يجب أن يكون لدينا علامة override / implement صارمة (أريد ذلك تمامًا) ، هل يجب علينا فرض الكلمة الرئيسية implement على أعضاء الواجهة الإلزامية؟ لأنه لا يساعد كثيرًا (لا يمكنك أن تفشل في تنفيذ ذلك لأنه بخلاف ذلك لن يتم تجميعه) وقد يؤدي إلى الكثير من الإسهاب غير الضروري. ولكن من ناحية أخرى ، قد يكون من الخداع أن يكون لديك implement على بعض أعضاء الواجهة ولكن ليس عليهم جميعًا.

JabX أريد شخصيًا تحذيرًا إذا أضافت فئة أساسية تطبيقًا لطريقة مجردة.

ومع ذلك ، فأنا لست مبيعًا بنسبة 100٪ على فكرة وجود كلمة رئيسية للأدوات في المقام الأول. سيحذرك المترجم بالفعل إذا لم تنفذ شيئًا. المكان الوحيد الذي يكون مفيدًا حقًا هو الأساليب الاختيارية.

وعلى أي حال ، فإن الأمر لا علاقة له بأسباب وجودي في هذا الموضوع. كنت أبحث فقط عما إذا كانت هناك طريقة لتحديد وظيفة كتجاوز لفصل أصلي.

سؤالي الرئيسي الآن هو ، هل يجب أن يكون لدينا علامة تجاوز / تنفيذ صارمة (أريد ذلك تمامًا) ، هل يجب أن نفرض الكلمة الأساسية للتنفيذ على أعضاء الواجهة الإلزامية؟

أعتقد أن عدم كتابته يجب أن يكون تحذيرًا.

المكان الوحيد الذي يكون مفيدًا حقًا هو الأساليب الاختيارية.

نعم ، هذا نوع من بيت القصيد هنا.

بدون هذا ، من الشائع جدًا استخدام طريقة الخطأ المطبعي التي ترغب في تجاوزها ولكنك في الحقيقة تقوم بإنشاء طريقة جديدة. إن تقديم مثل هذه الكلمات الرئيسية هو الطريقة الوحيدة للإشارة إلى نية تجاوز الوظيفة.

قد يكون هذا تحذيرًا أو أيا كان ، لكنه مؤلم للغاية حاليًا.

أقوم بإضافة تعليق توضيحي للتجاوز إلى عرض فئة UML في alm.tools : rose:

image

المراجع https://github.com/alm-tools/alm/issues/84

لقد أضفت أيضًا مؤشر هامش لأعضاء الفصل يتجاوز عضوًا في الفئة الأساسية في alm .

overrides

المراجع https://github.com/alm-tools/alm/issues/111

قبول العلاقات العامة لحل محدد النطاق نعتقد أنه يحقق أكبر قيمة بأقل قدر من التعقيد:

  • الكلمة الرئيسية الجديدة override صالحة في طريقة الفئة وإعلانات الخصائص (بما في ذلك get / set)

    • يجب أن تحتوي جميع التوقيعات (بما في ذلك التنفيذ) على override إذا كان أحدها كذلك

    • يجب وضع علامة على $ get و set override إذا كان أي منهما

  • من الخطأ أن يكون لديك override إذا لم يكن هناك خاصية / طريقة للفئة الأساسية بهذا الاسم
  • مفتاح سطر الأوامر الجديد --noImplicitOverride (لا تتردد في تغيير الاسم هنا) يجعل override إلزاميًا للأشياء التي يتم تجاوزها

    • لا يؤثر رمز التبديل هذا في السياقات المحيطة (مثل ملفات declare class أو .d.ts)

خارج النطاق في هذا الوقت:

  • وراثة التوقيعات
  • الكتابة السياقية للمعلمات أو المُهيِّئات (جربت ذلك سابقًا وكانت كارثة)
  • الكلمة الأساسية المقابلة للإشارة إلى "أنا أقوم بتنفيذ عضو واجهة مع هذا الإعلان"

تضمين التغريدة
أحاول حاليًا تنفيذ هذا (بدأت للتو ، وسأرحب بأي مساعدة في هذا ، خاصة للاختبار) ، لكنني لست متأكدًا من أنني أفهم ما وراء

يجب أن تحتوي جميع التوقيعات (بما في ذلك التنفيذ) على override إذا كان أحدها كذلك

نظرًا لأنه لا يمكنك الحصول على توقيعات متعددة لطريقة ما في الفصل ، هل تتحدث عن سياق الوراثة؟ هل تقصد ذلك (بدون العلم الذي يفرض استخدام override ، وإلا فمن الواضح أنه خطأ)

class A {
    method() {}
}

class B extends A {
    override method() {}
}

class C extends B {
    method() {} 
}

يجب إخراج خطأ في الفئة C لأنني لم أكتب override قبل method ؟

إذا كان الأمر كذلك ، فلست متأكدًا من سبب رغبتنا في فرض ذلك. وهل يجب أن تعمل أيضًا في الاتجاه المعاكس؟ إخراج خطأ في الفئة B إذا قمت بتحديد override في C وليس في B؟

نعم ، أعتقد أنه ينطبق على توقيعات شجرة الميراث.
ولا أعتقد أنها ستنجح في الاتجاه المعاكس ، يجب أن تبدأ في فرض قاعدة التجاوز بمجرد استخدامها على مستوى في التسلسل الهرمي. في بعض الحالات ، قد يتم اتخاذ قرار تطبيق التجاوز على فئة مشتقة من فئة لا يملكها المطور. لذلك ، بمجرد استخدامه ، يجب أن ينطبق على جميع الفئات المشتقة فقط.

لدي سؤال آخر حول

يجب وضع علامة على $ get و set override إذا كان أي منهما

ماذا يحدث هناك إذا كان صفي الأصلي يعرّف جامعًا فقط وأريد أن أعرّف الواضع في صفي المشتق؟ إذا اتبعت هذه القاعدة ، فهذا يعني أنه يجب تعريف أداة الإعداد الخاصة بي على أنها تجاوز ، ولكن لا يوجد واضع في الفئة الأصلية ، ويجب أن يكون من الخطأ تجاوز شيء غير موجود. حسنًا ، من الناحية العملية (في تطبيقي الحالي الساذج) ، لا يوجد أي تناقض نظرًا لأن المُحضر والواضع لهما نفس الخاصية.
يمكنني حتى كتابة شيء مثل:

class A {
    get value() { return 1; }
}

class B extends 1 {
   override set value(v: number) {}
}

الذي يبدو خطأ بالنسبة لي.

نظرًا لأنه لا يمكنك الحصول على توقيعات متعددة لطريقة في الفصل

يمكن أن يكون لديك العديد من التوقيعات الزائدة لنفس الطريقة:

class Base { bar(): { } }

class Foo extends Base {
  // Must write 'override' on each signature
  override bar(s: string): void;
  override bar(s?: number): void;
  override bar(s: string|number) { }
}

أعتقد أن حدسك حول الحالات الأخرى صحيح.

فيما يتعلق بالأحرف والمحددات ، أعتقد أن override ينطبق فقط على خانة الملكية. من الواضح أن كتابة أداة تعيين متجاوزة بدون أداة حافظة مقابلة أمر مريب للغاية ، لكنها ليست أكثر أو أقل ريبة في وجود override . نظرًا لأننا لا نعرف دائمًا كيف تم تنفيذ الفئة الأساسية ، أعتقد أن القاعدة هي ببساطة أن override getter أو setter يجب أن تحتوي على خاصية الفئة الأساسية المقابلة ، وأنك إذا قلت override get { ، أي إعلان set يجب أن يحتوي أيضًا على override (والعكس صحيح)

حسنًا ، لم أكن أعلم أنه يمكننا كتابة تواقيع الأسلوب في فصول كهذه ، هذا أنيق جدًا.

أعتقد أن virtual سيكون منطقيًا أيضًا.
جميع الكلمات الرئيسية: virtual ، override ، final ستحدث فرقًا كبيرًا خاصةً عند إعادة بناء الصفوف.
أنا أستخدم الميراث بشكل كبير ، فمن السهل جدًا الآن تجاوز الطريقة "عن طريق الخطأ".
سيؤدي virtual أيضًا إلى تحسين التحسس ، حيث يمكنك اقتراح طرق مجردة / افتراضية فقط بعد تجاوز الكلمة الرئيسية.

من فضلك ، يرجى إعطاء الأولوية لهذه. شكرا!

أتفق مع pankleks - أنا أستخدم الميراث كثيرًا ، ويشعر أنه من الخطأ تجاوز وظيفة دون تحديد أي شيء بشكل مباشر.

ستكون كلمات "الظاهري" و "التجاوز" و "النهائية" مثالية.

هذه مسألة مهمة بالنسبة لي.

تضمين التغريدة

الكتابة السياقية للمعلمات أو المُهيِّئات (جربت ذلك سابقًا وكانت كارثة)

يمكنك وضع على ذلك؟ لقد وجدت هذه المشكلة بالفعل أثناء محاولتي معرفة سبب عدم استنتاج TS نوع المعلمات الخاصة بي في تجاوزات الطريقة ، والتي كانت مفاجأة بالنسبة لي. لا أرى متى تكون قائمة المعلمات غير المتطابقة صحيحة عند التجاوز ، أو نوع إرجاع غير متوافق.

تضمين التغريدة

أعتقد أن virtual سيكون منطقيًا أيضًا.

بطبيعته كل شيء "افتراضي" في JS. دعم final سيكون كافياً IMHO: إذا كان لديك طريقة (أو خاصية) لا يجب تجاوزها ، يمكنك وضع علامة عليها على هذا النحو لتشغيل خطأ المترجم عند انتهاك هذا. لا حاجة ل virtual إذن ، أليس كذلك؟ ولكن تم تتبع ذلك في الإصدار رقم 1534.

avonwyss هناك نوعان من المسألتين هنا.

عندما حاولنا كتابة _property initializers_ حسب السياق ، واجهنا بعض المشكلات الموضحة في https://github.com/Microsoft/TypeScript/pull/6118#issuecomment -216595207. نعتقد أن لدينا نهجًا جديدًا أكثر نجاحًا في # 10570

إعلانات الطريقة عبارة عن كرة مختلفة من الشمع وهناك بعض الأشياء الأخرى التي يجب اكتشافها ، مثل ما يجب أن يحدث هنا:

declare class Base {
  method(x: string): string[];
  method(x: number, count: number): number[];
}
class Derived extends Base {
  method(x) { // x: ???
    return x;
  }
}

قد نقرر فقط أن طرق التوقيع المفرد فقط تحصل على أنواع المعلمات من القاعدة. لكن هذا لن يجعل الجميع سعداء ولا يحل المشكلة المتكررة المتمثلة في "أريد أن يكون للطريقة المشتقة الخاصة بي نفس التوقيعات ولكن فقط توفر تطبيقًا مختلفًا".

حسنًا ، سيكون نوع x هو string | number في هذه الحالة ، لكنني أتفهم أنه قد يكون من الصعب جدًا معرفة ذلك باستمرار.

لكنك ربما لا تريد أن يكون النوع _ خارجيًا-المشاهدة_ x string | number - بافتراض أن Derived له نفس الدلالات مثل Base ، لن يكون ' يكون قانونيًا للاستدعاء باستخدام (3) أو ('foo', 3)

همهمة نعم ، فاتني ذلك.

أرى أنك اقترحت قصره على أساليب التوقيع الفردي ، لكنني أعتقد أنه يمكن توسيعه "بسهولة" ليشمل طرق "مطابقة التوقيع" ، كما في المثال الخاص بك:

declare class Base {
  method(x: string): string[];
  method(x: number, count: number): number[];
}
class Derived extends Base {
  method(x, count) { // x: number, count: number
    return [];
  }
}

لأنه لا يوجد سوى توقيع على الفئة الأساسية المطابقة.

سيكون هذا أفضل بكثير مما لدينا (لا شيء) ، ولا أعتقد أنه سيكون من الصعب القيام بذلك؟ (لا يزال بعيدًا عن نطاق هذه المشكلة وربما المزيد من العمل)

RyanCavanaugh شكرا على الرد. كما لوحظ ، أملي في override هو أن يقوم بفرز مشكلة المعلمة (ونوع الإرجاع). لكني لا أرى أن مثالك يمثل مشكلة ، نظرًا لأن الأحمال الزائدة يجب أن يكون لها دائمًا تنفيذ واحد "خاص" واحد متوافق مع جميع الأحمال الزائدة (وهذا هو السبب في أن الاقتراح المقدم من JabX لن يعمل من الناحية المفاهيمية).
لذلك ، سيكون لدينا شيء مثل هذا:

declare class Base {
  method(x: string): string[];
  method(x: number, count: number): number[];
  // implies: method(x: string|number, count?: number): string[]|number[]
  // or fancier: method<T extends string|number>(x: T, count?: number): T[]
}
class Derived extends Base {
  override method(x, count?) { // may only be called like the method on Base
    return [x];
  }
}

هذا المنطق موجود بالفعل ومن الواضح أن التجاوز سيكون متاحًا فقط لطريقة التنفيذ "الخاصة" ، وليس لعمليات التجاوز المميزة (تمامًا كما لا يمكنك تنفيذ حمل زائد واحد بشكل صريح على أي حال). لذلك لا أرى مشكلة هنا - أو هل فاتني شيء ما؟

لا تحتوي الطرق المصممة بحيث يتم تجاوزها عادةً على أحمال زائدة على أي حال ، لذا حتى الاستغناء عن الطريقة البسيطة وعدم استنتاج المعلمات وأنواع الإرجاع في حالة وجود حمولات زائدة سيكون أمرًا جيدًا ومفيدًا تمامًا. لذا ، حتى لو كان السلوك كما هو عند تجاوز الطرق المحملة بشكل زائد ، فستحصل على خطأ عند التشغيل في وضع --no-implicit-any وسيتعين عليك تحديد أنواع متوافقة ، قد يبدو هذا أسلوبًا جيدًا تمامًا.

ربما أفتقد شيئًا ما ، لكن في جافا سكريبت (وبالتالي تنميد الكتابة) لا يمكنك استخدام طريقتين بنفس الاسم - حتى لو كانت لهما توقيعات مختلفة؟

في C # ، يمكن أن يكون لديك

public string method(string blah) {};
public int method(int blah) {};

ولكن هنا لا يمكنك أبدًا الحصول على وظيفتين بنفس الاسم ، بغض النظر عن ما تفعله بالتوقيعات ... لذلك لن يكون أي من الأمثلة الخاصة بك ممكنًا على أي حال ، أليس كذلك؟ ما لم نتحدث عن امتدادات متعددة في نفس الفصل ... لكن هذا ليس ما تعرضه أمثلتك ، فربما لا؟ هل هذه ليست قضية؟

في الوقت الحالي ، أستخدم الميراث ، وهو أمر محبط للغاية ... أضطر إلى وضع تعليقات حول الوظائف للإشارة إلى أنها افتراضية (على سبيل المثال ، أتجاوزها في مكان ما) أو تجاوز (أي أن هذه الوظيفة يتجاوز وظيفة أساسية). سوبر مزعج وفوضوي! وغير آمن!

@ sam-s4s نعم ، من الممكن التصريح عن عدة تواقيع منطقية للحمل الزائد لطريقة ما ، لكن ينتهي بهم الأمر جميعًا في نفس التطبيق الفردي (والذي يتعين عليه بعد ذلك اكتشاف المعلمات التي تم تمريرها والتي يتم استدعاء "التحميل الزائد"). يتم استخدام هذا بشكل كبير من قبل العديد من أطر عمل JS ، على سبيل المثال jQuery حيث يضيف $(function) وظيفة جاهزة ، لكن $(string) يبحث في DOM عن طريق المحدد.

انظر هنا للحصول على المستندات: https://www.typescriptlang.org/docs/handbook/functions.html#overloads

avonwyss آه نعم ، الإعلانات ، ولكن ليس التنفيذ ، هذا منطقي :) شكرًا لك

لقد استمر هذا لفترة طويلة جدًا ، وأجد أنه من المزعج أنني يجب أن أقرأ هذا الخيط المتضخم بالكامل حتى أبدأ في معرفة سبب عدم احتواء TS على هذا التأكيد الأساسي لوقت التجميع.

أين نحن إما (أ) كلمة رئيسية override أو (ب) تعليق توضيحي @override jsdoc يخبر المترجم "يجب أن توجد طريقة لها نفس الاسم ونوع التوقيع على فئة فائقة." ؟

وبشكل أكثر تحديدًا ، هل يشير التعليق في https://github.com/Microsoft/TypeScript/issues/2000#issuecomment -224776519 (8 يونيو 2016) بقلم RyanCavanaugh إلى أن الخطوة التالية هي العلاقات العامة (المجتمع)؟

class Bar extends Foo {
  /**
   * <strong i="12">@override</strong>
   */
  public toString(): string {
     // ... 
  }

  override public toString(): string {
    // ...
  }
}

وبشكل أكثر تحديدًا ، هل يشير التعليق الموجود في # 2000 (تعليق) (8 يونيو 2016) بقلم RyanCavanaugh إلى أن الخطوة التالية هي (المجتمع) العلاقات العامة؟

pcj صحيح

لقد بدأت العلاقات العامة على هذا في # 13217. نرحب بأي شخص آخر مهتم بهذه الوظيفة للمشاركة و / أو تقديم اقتراحات. شكرا!

أفضل نفس الشيء بالنسبة لجافا مع مصمم

<strong i="6">@Override</strong>
public toString(): string {
   // ...
}

@ Chris2011 توافق على أن هذا يبدو مألوفًا ، ولكن هذا يعني كمصمم أن @Override سيتم تقييمه في وقت التشغيل ، وليس وقت الترجمة. أخطط لـ javadoc /** <strong i="7">@override</strong> */ public override toString() علاقات عامة منفصلة بمجرد الانتقال إلى الأمام في المراجعة.

تحاول متابعة محادثة هذا الموضوع. هل يشمل هذا وظائف ثابتة؟ لا أرى أي سبب يمنعنا من السماح أيضًا بتجاوز الوظائف الثابتة في الفئات المشتقة ذات التوقيعات المختلفة تمامًا. هذا مدعوم في ES6. إذا كان class A { } ; A.create = function (){} ثم class B extends A { } ; B.create = function (x,y) { } ، فإن استدعاء A.create() لن يستدعي B.create() ويسبب مشكلات. لهذا السبب (ولإنشاء القدرة على استخدام نفس اسم الوظيفة على الأنواع كوظائف المصنع) ، يجب أيضًا السماح بتجاوز توقيع الوظائف الثابتة الأساسية. إنه لا يكسر أي شيء ، ويضيف القدرة على القيام ببعض الأشياء الأنيقة (خاصة بالنسبة لأطر محرك اللعبة ، حيث يكون أي شيء "جديد" شريرًا حقًا إذا تم استخدامه طوال الوقت دون الانسحاب من ذاكرة التخزين المؤقت للكائنات لتقليل تجميد GC). نظرًا لأن المنشئات القابلة للاستدعاء غير مدعومة ضمن ES6 ، فإن إنشاء اصطلاح تسمية مشترك للطرق على الأنواع هو الخيار الآخر الوحيد ؛ ومع ذلك ، يتطلب القيام بذلك حاليًا الوظيفة الثابتة المشتقة لإظهار الحمل الزائد لتوقيع الوظيفة الثابتة الأساسي جنبًا إلى جنب مع توقيعها الخاص ، وهو أمر غير مفيد لوظيفة المصنع على النوع الذي يتعامل فقط مع هذا النوع. : / في هذه الأثناء ، الخيار الوحيد الذي أملكه هو إجبار مستخدمي إطار العمل الخاص بي على تكرار جميع تواقيع الوظائف الثابتة للتسلسل الهرمي الأساسي (مثل SomeType.create() ) على الأنواع المشتقة ، وما إلى ذلك ، الأمر الذي يصبح سخيفًا حقًا .

فيما يلي مثال عن "السخف" الذي أتحدث عنه (وهو أمر ناجح ، ولكن ليس شيئًا سأفتخر به في إطار عمل ممتد):

class A {
    static create(s: string) {
        var inst: A;
        /* new or from cache */
        inst.init(s);
    }
    protected init(s: string) { }
}

class B extends A {
    static create(s: string);
    static create(n: number);
    static create(n:any) {
        var inst: B;
        /* new or from cache */
        inst.init(n);
    }
    protected init(s: string);
    protected init(n: number);
    protected init(n: any) {
        super.init(n.toString());
    }
}

class C extends B {
    static create(s: string)
    static create(n: number)
    static create(b: boolean)
    static create(b: any) {
        var inst: C;
        /* new or from cache */
        inst.init(b);
    }
    protected init(s: string);
    protected init(n: number);
    protected init(b: boolean);
    protected  init(b: any) {
        super.init(b ? 0 : 1);
    }
}

(https://goo.gl/G01Aku)

كان من الممكن أن يكون هذا أجمل بكثير:

class A {
    static create(s: string) {
        var inst: A;
        /* new or from cache */
        inst.init(s);
    }
    protected init(s: string) { }
}

class B extends A {
    new static create(n:number) {
        var inst: B;
        /* new or from cache */
        inst.init(n);
    }
    new protected init(n: number) {
        super.init(n.toString());
    }
}

class C extends B {
    new static create(b: boolean) {
        var inst: C;
        /* new or from cache */
        inst.init(b);
    }
    new protected  init(b: boolean) {
        super.init(b ? 0 : 1);
    }
}

rjamesnw ، قد تكون مهتمًا بـ "هذا" متعدد الأشكال للأعضاء الساكنين الذين لديهم عقد لوظائف المصنع تتم مناقشته كحالة استخدام أولية في جميع التعليقات.

لذلك ، مرة أخرى ، إعادة التعبير عن الشعور الذي عبرت عنه pcj ( تعليق ) منذ أكثر من عام ، من المحير أن تضطر إلى قراءة الكثير من العلاقات العامة والتعليقات هنا وفي أي مكان آخر لتحديد مكان طلب الميزة هذا.

يبدو أن # 13217 كان قريبًا جدًا ، ثم تم إسقاطه مرة أخرى بواسطة DanielRosenwasser والشركة كميزة قد لا تتناسب مع اللغة ، ويبدو أن إعادة الدخول في المحادثة الدائرية هنا حول هذه المشكلة حول ما إذا كان ينبغي القيام بذلك أم لا. ربما هذا الشيء "وقت التصميم" (# 2900) سيحلها ، ربما لا؟ سيكون من الجيد معرفة ذلك.

في ما يلي بعض أوجه القصور الأخرى في نهج مصمم وقت التشغيل الذي تم نشره قبل بضع سنوات ولم أشاهده مذكورًا:

  • لن تعمل مع الخصائص لأنها ليست جزءًا من النموذج الأولي
  • يمكن للخصائص والمنافذ (نوع من ...) تجاوز بعضها البعض ، وهو ما لن يعمل أيضًا
  • لدينا فصول مجردة الآن ، ولكن نظرًا لأن الأشياء المجردة لا توجد فعليًا في وقت التشغيل ، فلا يمكن أن تعمل معها

إذا كان التحذير الوحيد هو أن عمليات التحقق حدثت عند تهيئة الفصل ، فمن المحتمل أن أقبلها كإجراء مؤقت ، ولكن كما هو الحال هناك الكثير من القيود.

بصراحة لا أصدق أن هذه الميزة لا تزال مثيرة للجدل على مدار 3 سنوات منذ أن سجلت المشكلة لأول مرة. كل لغة "جادة" موجهة للكائنات تدعم هذه الكلمة الأساسية ، C # ، F # ، C ++ ... سمها ما شئت.

يمكنك مناقشة الافتراضات طوال اليوم حول سبب اختلاف جافا سكريبت وتحتاج إلى نهج مختلف. ولكن من منظور عملي يعمل يوميًا في قاعدة شفرة كبيرة جدًا من النوع ، يمكنني أن أخبرك أن التجاوز سيحدث فرقًا كبيرًا في قابلية قراءة الكود وصيانته. سيؤدي ذلك أيضًا إلى القضاء على فئة كاملة من الأخطاء بسبب الفئات المشتقة التي تتخطى بطريق الخطأ طرق الفئة الأساسية بتوقيعات متوافقة ولكن مختلفة تمامًا.

أرغب حقًا في رؤية التنفيذ المناسب لـ Virtual / override / final. سأستخدمها طوال الوقت ، وستجعل الكود أكثر قابلية للقراءة وأقل عرضة للخطأ. أشعر أن هذه ميزة مهمة.

متفق. إنه لأمر محبط أن نرى كيف تتم إضافة ميزات غامضة / متطورة نسبيًا ، بينما يتم رفض شيء ما ... أساسي. هل هناك أي شيء يمكننا القيام به للضغط من أجل هذا؟

هيا أيها الناس! إذا كان هناك العديد من التعليقات وأكثر من ثلاث سنوات ، فلماذا لم يتم تنفيذها بالفعل!

تعال: joy_cat:

يرجى أن تكون بناءة ومحددة ؛ لا نحتاج إلى العشرات من التعليقات برجاء فعل ذلك بالفعل

لكن يا رفاق ، من فضلك كن محددًا أيضًا إلى جانبك.

من الواضح أن المجتمع مهتم جدًا بهذه الميزة ، ولا يقدم لنا فريق TS أي تفاصيل حول مستقبل هذه الميزة.

أنا شخصياً أتفق تمامًا مع armandn ، الإصدارات الأخيرة من TS تجلب ميزات نادرًا ما تستخدم نسبيًا أثناء الاحتفاظ بشيء مثل هذا.

إذا كنت لا تخطط للقيام بذلك ، فقط أخبرنا. عدا ذلك ، يرجى إخبارنا كيف يمكن للمجتمع أن يساعد.

مجرد مخطط زمني هنا نظرًا لوجود تعليقات أكثر مما يرغب GitHub في عرضه عند التحميل:

ليس الأمر كما لو أننا لا نأخذ في الاعتبار هنا. هذا أقرب ما يكون إلى دخول الميزات ، وقد رفضنا أفكار الميزات الخاصة بنا لأسباب مماثلة - راجع # 24423 للحصول على مثال حديث.

نريد حقًا تنمية اللغة عن قصد. هذا يستغرق وقتًا ، ويجب أن تتوقع التحلي بالصبر. بالمقارنة ، C ++ أقدم من كثير من الأشخاص في هذا الموضوع ؛ TypeScript ليس قديمًا بما يكفي ليكون في المنزل بدون إشراف شخص بالغ. بمجرد إضافة شيء ما إلى اللغة ، لا يمكننا إزالته مرة أخرى ، لذلك يجب موازنة كل إضافة بعناية مقابل إيجابياتها وسلبياتها. أتحدث من تجربة أن الميزات التي كان الناس يصرخون من أجلها (طرق التمديد ، أنا أنظر إليك) كانت ستدمر قدرتنا على مواصلة تطوير اللغة (لا توجد أنواع تقاطعات ، ولا أنواع اتحادات ، ولا أنواع مشروطة) لو قمنا بتنفيذها لمجرد كان هناك الكثير من تعليقات GitHub. أنا لا أقول أن override أمر خطير لإضافته ، فقط لأننا نتعامل دائمًا مع هذا بأقصى قدر من الحذر.

إذا اضطررت إلى تلخيص المشكلات الأساسية مع override الآن:

  • لا يسمح لك بفعل أي شيء لا يمكنك فعله اليوم مع مصمم الديكور (لذا فهو "ليس جديدًا" من حيث "الأشياء التي يمكنك إنجازها")
  • لا يمكن أن تتوافق الدلالات بدقة مع override في اللغات الأخرى (يزيد العبء المعرفي)
  • لا "تضيء" بنسبة 100٪ بدون علامة سطر أوامر جديدة ، وستكون علامة ربما نريد أن تكون أقل من strict ولكن في الواقع لا يمكننا ذلك لأنها ستكون كبيرة جدًا تغيير كسر (ينقص القيمة المسلمة). وأي شيء يشير إلى علامة سطر أوامر جديدة هو مضاعفة أخرى لمساحة التكوين التي نحتاج إلى تقييمها بجدية ، لأنه يمكنك فقط مضاعفة شيء ما عدة مرات قبل أن يستهلك ميزانيتك العقلية بالكامل عند اتخاذ قرارات مستقبلية
  • خلاف داخلي قوي إلى حد ما هنا حول ما إذا كان يمكن تطبيق override لتطبيق عضو واجهة (يقلل من القيمة المتصورة لأن التوقعات لن تتطابق مع الواقع بالنسبة لنسبة معينة من الأشخاص)

الجانب الإيجابي الإجمالي هنا هو أنه يمكنك أ) الإشارة إلى أنك تجاوزت شيئًا ما (وهو ما يمكنك فعله اليوم بتعليق) ، ب) اكتشاف خطأ إملائي كان من المفترض أن يساعدك الإكمال التلقائي فيه على أي حال ، و ج) بعلامة جديدة ، التقاط الأماكن التي "نسيت" وضع كلمة رئيسية فيها وتحتاج الآن إلى (مهمة ميكانيكية بسيطة من غير المرجح أن تجد أخطاء حقيقية). لقد فهمت أن ب) محبط للغاية ولكن مرة أخرى ، نحتاج إلى تلبية شريط التعقيد هنا.

في نهاية اليوم ، إذا كنت تعتقد أن فصول JS ستساعد بشكل كبير من خلال كلمة رئيسية override ، فإن مناصرة اقتراح TC39 لإضافته إلى وقت التشغيل الأساسي سيكون مكانًا جيدًا للبدء.

لا يسمح لك بفعل أي شيء لا يمكنك فعله اليوم مع مصمم الديكور (لذا فهو "ليس جديدًا" من حيث "الأشياء التي يمكنك إنجازها")

ربما أسيء فهم هذا ، ولكن هناك أشياء مهمة جدًا لا يستطيع المصمم القيام بها والتي يمكن للكلمة الرئيسية القيام بها. لقد ذكرت البعض في https://github.com/Microsoft/TypeScript/issues/2000#issuecomment -389393397. صححني بالتأكيد إذا كانت هذه الأشياء ممكنة ، لأنني أرغب في استخدام نهج المصمم ولكن الطرق غير المجردة ليست سوى جزء صغير من حالة استخدامي لهذه الميزة.

هناك جانب إيجابي آخر لم يتم ذكره وهو أن كلمة رئيسية للتجاوز ستجعل تغيير شيء ما في فئة أساسية أكثر جدوى. من الصعب تغيير اسم أو تقسيم شيء ما إلى جزأين وما إلى ذلك ، مع العلم أن أي فئات مشتقة سيتم تجميعها بشكل جيد دون تحديثها للمطابقة ولكنها ربما تفشل في وقت التشغيل. أيضًا ، مع الأخذ في الاعتبار أنه لا توجد كلمات رئيسية نهائية / مختومة / داخلية متاحة ، فقد يتم اشتقاق أي فئة مُصدرة تقريبًا في مكان ما مما يجعل تغيير أي شيء غير خاص أكثر خطورة مما سيكون عليه مع إتاحة التجاوز.

انطباعي هو أن قاعدة TSLint ستكون إلى حد بعيد الطريق الأكثر سلاسة إلى الأمام هنا. ذكر kungfusheep الفكرة باختصار في https://github.com/Microsoft/TypeScript/issues/2000#issuecomment -192502734 ، لكني لا أرى أي متابعة. هل هناك أي شخص آخر على علم بتنفيذ TSLint لهذه الميزة؟ إذا لم يكن الأمر كذلك ، فمن المحتمل أن أبدأ في اختراق أحدها عندما تسنح لي الفرصة. 😄

فكرتي الحالية هي أنه سيكون مجرد تعليق ، // @override ، تمامًا مثل // @ts-ignore والعديد من التوجيهات الأخرى المستندة إلى التعليقات التي رأيتها. أنا شخصياً أفضل الابتعاد عن المصممين لأنهم (في شكلهم الحالي) لديهم دلالات وقت التشغيل وبما أنهم لا يزالون في المرحلة 2 ويتم تعطيلهم افتراضيًا.

مع أي حظ ، ستكون قاعدة TSLint المخصصة حلاً بنسبة 90٪ ، وهي تفتقر حقًا إلى الجماليات ، وبالطبع يمكنها المضي قدمًا دون الحاجة إلى الالتزام بأي تفاصيل عن اللغة. مع القواعد المدركة للكتابة وخدمة tslint-language-service والتثبيت التلقائي ، لا يوجد فرق كبير بين خطأ TSLint وخطأ TS المدمج من منظور المطور. آمل أن يمنح المكون الإضافي TSLint (أو المكونات الإضافية المتنافسة المتعددة) المجتمع بعض الخبرة وفرصة للتوصل إلى اتفاق حول أفضل الدلالات. ثم ربما يمكن إضافتها كقاعدة أساسية لـ TSLint ، وربما يوفر ذلك وضوحًا وتحفيزًا كافيين لتبرير المنفعة الجمالية لكلمة رئيسية override في اللغة الأساسية.

مجرد التفكير خارج الصندوق هنا. لدينا سيناريوهان مختلفان هنا. يستخدم C # (كمثال فقط) virtual و override لأن الأساليب ليست افتراضية بشكل افتراضي. في JavaScript ، جميع الوظائف في الفصل هي virtual افتراضيًا (حسب الطبيعة). أليس من المنطقي إذن عكس العملية والحصول على معدِّل نوع nooverride بدلاً من ذلك؟ بالطبع لا يزال بإمكان الناس إجبارها ، ولكن على الأقل هناك للمساعدة في دفع اتفاقية تقضي بعدم المساس ببعض الوظائف في الفئات الأساسية. مرة أخرى ، مجرد التفكير خارج القاعدة هنا. ؛) قد يكون أيضًا أقل من تغيير جذري.

أليس من المنطقي إذن عكس العملية والحصول على معدِّل نوع nooverride بدلاً من ذلك؟

تعجبني طريقة تفكيرك وأعتقد أن ما تبحث عنه نهائي .

ولكن ماذا عن readonly ؟ أعتقد أن تجاوز طريقة في JS يعني في الواقع استبدال الوالدين بالطفل أثناء إنشاء مثيل (عندما يتم تطبيق سلسلة النموذج الأولي). في هذه الحالة ، فإن وجود طريقة readonly تعني "إنها موجودة ليراها الجميع ، لكن لا أريد أن يغيرها أي شخص ، سواء عن طريق الوراثة أو ديناميكيًا بعد إنشاء مثيل" أمر منطقي للغاية. لقد تم تطبيقه بالفعل على الأعضاء ، فلماذا لا يتم تطبيقه للطرق أيضًا؟

هل يوجد بالفعل اقتراح لذلك؟ إذا لم يكن الأمر كذلك ، فقد يكون من المفيد التحقيق كبديل للتجاوز ...

_edit: _ اتضح أنه يمكنك تجاوز عضو للقراءة فقط ، لذلك تنهار هذه الحجة بأكملها.

ربما السماح بوظائف فئة private هو خيار آخر؟

تحرير: كنت أفكر "بدلاً من وضع علامة عليها نهائيًا" ولكن يجب أن أكون نصف نائم (من الواضح أن كلمة "نهائي" تعني عامة ولكن لا يمكن تجاوزها) ، LOL ؛ لا يهم.

rjamesnw يمكنك بالفعل تحديد وظائف الفئة على أنها عامة ومحمية وخاصة.

لا أعتقد أن مجرد الحصول على "نهائي" سيكون حلاً. تكمن المشكلة في أن الأشخاص يمكنهم عن طريق الخطأ إنشاء وظيفة جديدة في فئة ترث من فئة أساسية بهذا الاسم قيد الاستخدام بالفعل ، وبعد ذلك ستقوم بتقسيم الأشياء بصمت دون معرفة السبب. لقد حدث لي عدة مرات ، محبط للغاية ، لأنك في كثير من الأحيان لا تحصل على أخطاء ، وتحدث أشياء غريبة (أو لا تحدث) ...

لذلك أعتقد ، حقًا ، أننا نبحث في إدخال tsconfig.json جديد سيجبر المترجم على إلقاء خطأ عندما يتم تجاوز شيء ما دون تمييزه على أنه افتراضي (ويتم وضع علامة تجاوز أو نهائي).

أعتقد أنه بدون override ، فإن TypeScript تخيب آمال مستخدميها بشأن وعدها [المتصور] بسلامة وقت التجميع وأمان الكتابة.
تنقسم الوسيطة "استخدام أمر IDE لتجاوز الطريقة" مع تطور الكود (كما أشار أشخاص آخرون).
يبدو أيضًا أن جميع اللغات الرئيسية القابلة للمقارنة قد أضافت override بشكل ما.
لعدم إجراء تغيير جذري ، يمكن أن تكون قاعدة tslint (كما هو الحال في Java).
راجع للشغل ، لماذا لا نسمح بفصل التغييرات بين لغة الإصدارات الرئيسية ، مثل 2.x -> 3.x. لا نريد أن نتعثر ، أليس كذلك؟
إذا اتضح أن هناك خطأ ما في تفاصيل معينة override ، ويتطلب بعض التغيير والتبديل في 3.x ، فليكن. أعتقد أن معظم الناس سيتفهمون ويقدرون المقايضة بين سرعة التطور مقابل التوافق. حقًا بدون override لا يمكنني أن أوصي بـ TS كشيء عملي أكثر من Java أو C # ...

إذا أراد الأشخاص أن يكونوا صارمين ، فيجب أن تكون هناك طريقة إلزامية override (يمكن أن تكون قاعدة tslint اختيارية). على الرغم من أن قيمة override الإلزامية أقل بكثير ، لأن فرصة تجاوز طريقة من الطبقة الفائقة عن طريق الخطأ تبدو أقل بكثير من عدم التجاوز عن طريق الخطأ.

حقًا ، هذه المشكلة التي تم فتحها منذ عام 2015 هي عبارة عن دلو من الماء البارد لحماسي في TypeScript وكرازي ...

هذا لا يعالج طرق التجاوز من الأجداد * - الآباء:

/* Put this in a helper library somewhere */ function override(container, key, other1) { var baseType = Object.getPrototypeOf(container); if(typeof baseType[key] !== 'function') { throw new Error('Method ' + key + ' of ' + container.constructor.name + ' does not override any base class method'); } }

إنه لأمر محزن ومحبط أيضًا أن No one assigned على GH هنا بشأن هذه المسألة. ربما حان الوقت لشوكة TypeScript؟ ؛) CompileTimeSafeScript ...

هذا الخيط بأكمله يبدو وكأنه نمط مضاد "للشلل عن طريق التحليل". لماذا لا تكتفي "80٪ من الحالات بقيمة 20٪ من العمل" وتجربها عمليًا ، وإذا لزم الأمر ، قم بتعديلها في 3.x؟

حالات الاستخدام البسيطة والمتكررة والقيمة للغاية تكون "رهينة" في بعض الحالات الجانبية التي لن يحتاج معظم الناس أبدًا إلى القلق بشأنها.

لذلك ، من الممكن التحقق من الكتابة في وقت الترجمة عبر أدوات الديكور ، وإن لم يكن ذلك بدون القليل من المساعدة:

export const override = <P extends Function>() => <K extends keyof P["prototype"]>(
  target: Object,
  methodName: K,
  descriptor: TypedPropertyDescriptor<P["prototype"][K]>
) => {
  // this is a no-op. The checking is all performed at compile-time, so runtime checks are not needed.
}

class Bar {
  biz (): boolean {
    return true;
  }

  qux (): string {
    return "hi";
  }
}

class Foo extends Bar {
  // this is fine
  @override<typeof Bar>()
  biz (): boolean {
    return false;
  }

  // error: type '() => number' is not assignable to type '() => boolean'
  @override<typeof Bar>()
  biz (): number {
    return 5;
  }

  // error: argument of type '"baz"' is not assignable to parameter of type '"biz" | "qux"'
  @override<typeof Bar>()
  baz (): boolean {
    return false;
  }
}

لست متأكدًا مما إذا كان من الممكن الحصول على إشارة إلى النوع الممتاز دون تمريره صراحةً. هذا يكلف وقت تشغيل ضئيلًا ، لكن الفحص يستغرق وقتًا للترجمة.

تضمين التغريدة

انطباعي هو أن قاعدة TSLint ستكون إلى حد بعيد الطريق الأكثر سلاسة إلى الأمام هنا.

[...]

ثم ربما يمكن إضافتها كقاعدة TSLint أساسية ، وربما يوفر ذلك وضوحًا وتحفيزًا كافيًا لتبرير الفائدة الجمالية لكلمة رئيسية تجاوز في اللغة الأساسية.

وافق 100٪. لنبدأ بالفعل!

مرحبًا - متأخر قليلاً عما هو مخطط له ولكن إليك قاعدة tslint التي تم تنفيذها باستخدام مصمم.

https://github.com/bet365/override-linting-rule

لقد استخدمنا هذا عبر قاعدة شفرة TypeScript 1mloc تقريبًا لبضع سنوات وقمنا بتحديثها مؤخرًا لاستخدام خدمة اللغة ، مما يجعلها أسرع بكثير في التنفيذ وأكثر جدوى من فتح المصدر (تطلب التكرار السابق تمريرًا كاملاً للمشروع لجمع معلومات التراث).

بالنسبة لحالة الاستخدام الخاصة بنا ، فقد كان لا يقدر بثمن - على الرغم من أننا ما زلنا نعتقد أن المسؤولية يجب أن تقع في النهاية على عاتق المترجم وبالتالي يجب اعتبار قاعدة الفحص بمثابة نقطة توقف.

شكرا

أنا موافق. أعتقد أيضًا أن الحل الذي يتضمن مترجم TS سيكون أفضل بكثير من قواعد الديكور والنسالة.

ما هي حالة هذه الميزة؟ هل تمت إعادة النظر فيه كميزة مترجم حتى الآن؟

لذلك ، من الممكن التحقق من الكتابة في وقت الترجمة عبر أدوات الديكور ، وإن لم يكن ذلك بدون القليل من المساعدة:

بعض التحسينات:

function override< Sup >( sup : { prototype : Sup } ) {
    return <
        Field extends keyof Sup ,
        Proto extends { [ key in Field ] : Sup[ Field ] } ,
    >(
        proto : Proto ,
        field : Field ,
        descr : TypedPropertyDescriptor< Sup[ Field ] > ,
    )=> {}
}

class Foo {

    bar( a : number ) {
        return a 
    }

    bar2( a : number , b : number ) {
        return a 
    }

}

class Foo2 {

    @override( Foo )
    bar( a : number ) {
        return 1
    }

    @override( Foo )
    bar2( a : number , b : number ) {
        return 1 
    }

    xxx() { return '777' }

}

class Foo3 extends Foo2 {

    @override( Foo ) // OK
    bar( a : number ) { return 5 }

    @override( Foo ) // Error: less args than should
    bar2( a : number ) { return 5 }

    @override( Foo ) // Error: accidental override Foo2
    xxx() { return '666' }

    @override( Foo ) // Error: override of absent method
    yyy() { return 0 }

}

ملعب

حسنًا ، اكتشف حل التجاوز الذي يمنع حدوث خطأ في الترجمة.
مثال مع تيار Node Readable:

// Interface so you will keep typings for all Readable methods/properties that are not overriden:
// Fileds that are `Omit`-ed should be overriden (with any signature you want, it do not have to be compatible with parent class)
interface ReadableObjStream<T> extends Omit<stream.Readable, 'push' | 'read'> {}

// Use extends (TYPE as any) to avoid compilation errors and override `Omit`-ted methods
class ReadableObjStream<T> extends (stream.Readable as any) {
    constructor()  {
        super({objectMode: true}); // force object mode. You can merge it with original options
    }
    // Override `Omit`-ed methods with YOUR CUSTOM SIGNATURE (can be non-comatible with parent):
    push(myOwnNonCompatibleSignature: T): string  { /* implementation*/ };
    read(options_nonCompatibleSignature: {opts: keyof T} ): string  { /* implementation*/ }
}

let typedReadable = new ReadableObjMode<{myData: string}>();
typedReadable.push({something: 'else'}); // will throw compilation error as expected
typedReadable.pipe(...) // non overloaded methods typings supported as expected

العيب الوحيد لهذا الحل هو نقص الكتابة عند استدعاء super.parentMethod (ولكن بفضل interface ReadableObjStream ، لديك كل الكتابات عند استخدام مثيل ReadableObjStream.

@ nin- jinbioball شكرًا على المساهمات مع التحقق من وقت الترجمة المصمم.

لسوء الحظ ، لا يبدو أنه يعمل مع أعضاء protected ، فقط مع public .

(مثال على خطأ في مفترقتي لملعب نين جين

كان محدد التجاوز ميزة قاتلة في c ++ 11.
إنه يساعد كثيرًا ويحمي كود إعادة البناء.
أود بالتأكيد الحصول على هذا في دعم قاعدة TS دون أي عقبات (تصويت!)

لقد أظهرنا بالتأكيد دعمًا كبيرًا لهذه الميزة ، ولكن يبدو أنهم ما زالوا لا يخططون لإضافة هذه الميزة في أي وقت قريبًا.

فكرة اخرى:

class Obj { 

    static override<
        This extends typeof Obj,
        Over extends keyof InstanceType<This> = never,
    >(this: This, ...overs: Over[]) { 
        return this as This & (
            new(...a:any[])=> InstanceType<This> & Protect< Omit<InstanceType<This> , Over > >
        )
    }

}

class Foo extends Obj {

    bar(a: number) {
        return 0
    }

    bar2(a: number) {
        return 0
    }

    foo = 1

}

class Foo2 extends Foo.override('bar') {

    foo = 2

    bar( a : number ) {
        return 1
    }

    // Error: Class 'Foo & Protect<Pick<Foo, "bar2" | "foo">>'
    // defines instance member property 'bar2',
    // but extended class 'Foo2' defines it as instance member function.
    bar2( a : number ) {
        return 1
    }

    bar3( a : number ) {
        return 1
    }

}

declare const Protected: unique symbol

type Protect<Obj> = {
    [Field in keyof Obj]:
    Object extends () => any
    ? Obj[Field] & { [Protected]: true }
    : Obj[Field]
}

رابط الملعب

إضافة سنتي هنا.
لدينا مكون زاوي نموذجي ، يتعامل مع النماذج ويحتاج إلى إلغاء الاشتراك من valueChanges وما شابه. لم نرغب في تكرار الكود في كل مكان (الحالة الحالية كيندا) ، لذلك قمت بعمل "TypicalFormBaseComponent" (من بين أشياء أخرى) ينفذ أساليب الزوايا OnInit و OnDestroy. تكمن المشكلة الآن في أنك إذا كنت تستخدمها بالفعل وأضفت طريقة OnDestroy الخاصة بك (وهو أمر قياسي جدًا يجب القيام به) ، فإنك تخفي الطريقة الأصلية وتكسر النظام. استدعاء super.OnInit يصلح ، لكن لا توجد آلية حاليًا لإجبار الأطفال على القيام بذلك.

إذا قمت بذلك مع المُنشئ ، فإنه يجبرك على استدعاء super () ... كنت أبحث عن شيء مشابه ، ووجدت هذا الموضوع ، وبصراحة ، أشعر بالضيق نوعًا ما.
قد يكون تنفيذ "جديد" و "تجاوز" في الملخصات بمثابة تغيير جذري ، ولكن إصلاحه سيكون بسيطًا للغاية في أي قاعدة بيانات (فقط إضافة "تجاوز" في كل مكان حيث يصرخ). قد يكون أيضًا مجرد تحذير .

على أي حال ، هل هناك طريقة أخرى بالنسبة لي لفرض المكالمة الفائقة على الأطفال؟ حكم TSLint الذي يمنع الاختباء أم شيء من هذا القبيل؟

ملاحظة: أنا لا أتفق مع سياسة "لا تعليق". يمنع الناس من إلقاء الأمثلة هنا. ربما لن تنفذ "تجاوز" ، لكنك ستنفذ شيئًا آخر ، يعتني بمشاكلهم ... أي إذا كان بإمكانك قراءتها بالفعل.

GonziHere في لغات أخرى مثل C # أو Java ، لا يعني التجاوز شرط استدعاء الطريقة الفائقة - غالبًا ما لا يكون هذا هو الحال في الواقع. المنشئون هم "طرق افتراضية" خاصة وليست عادية.

سيتم استخدام الكلمة الأساسية override لتحديد أن الطريقة يجب أن يتم تعريفها بالفعل بتوقيع متوافق على الفئة الأساسية ، ويمكن للمجمع أن يؤكد ذلك.

نعم ، لكن الحاجة إلى التجاوز تجبرك على ملاحظة وجود الطريقة.

GonziHere يبدو أن ما قد تحتاجه حقًا هو إنشاء فئات أساسية مجردة بوظائف مجردة. ربما يمكنك إنشاء طرق _onInit و _onDestroy خاصة يمكنك الاعتماد عليها أكثر ، ثم إنشاء وظائف مجردة onInit و onDestroy محمية (أو وظائف عادية إذا كانت كذلك لا مطلوب). ستستدعي الوظائف الخاصة الآخرين ، ثم تكتمل كالمعتاد.

GonziHererjamesnw الشيء الآمن الذي يجب فعله هو في الواقع فرض أن onInit و onDestroy هما _final_ ، وتحديد طرق القالب المجرد المحمية الفارغة التي تستدعيها الطرق النهائية. يضمن هذا عدم قدرة أي شخص على تجاوز الأساليب عن طريق الخطأ ، ومحاولة القيام بذلك (بافتراض وجود طريقة لفرض الطرق النهائية) ستوجه المستخدمين على الفور إلى الطريقة الصحيحة لتنفيذ ما يريدون.

shicks ، rjamesnw ، أنا في نفس الوضع مثلGonziHere. لا أريد أن يكون للفئة الأساسية طرق مجردة لأن هناك وظائف أريد تنفيذها لكل من خطافات دورة الحياة هذه. المشكلة هي أنني لا أريد أن يتم استبدال هذه الوظيفة الأساسية عن طريق الخطأ عندما يضيف شخص ما ngOnInit أو ngOnDestroy في الفصل الفرعي دون استدعاء super() . سيؤدي طلب override إلى لفت انتباه المطور إلى وجود هذه الأساليب في الفئة الأساسية وأنه يجب عليهم اختيار ما إذا كانوا بحاجة إلى الاتصال بـ super() ؛

من واقع خبرتي في كتابة Java ، فإن @Override شائع جدًا لدرجة أنه يصبح ضوضاء. في كثير من الحالات ، تكون الطريقة المتجاوزة مجردة أو فارغة (حيث يجب ألا يتم استدعاء super() ) ، مما يجعل وجود override إشارة واضحة بشكل خاص إلى الحاجة إلى super() .

من واقع خبرتي في كتابة Java ، فإن @Override شائع جدًا لدرجة أنه يصبح ضوضاء. في كثير من الحالات ، تكون الطريقة المتجاوزة مجردة أو فارغة (حيث يجب ألا يتم استدعاء super() ) ، مما يجعل وجود override إشارة واضحة بشكل خاص إلى الحاجة إلى super() .

هذا لا علاقة له بالموضوع المطروح. قد يُقال أيضًا أنك تستخدم التكوين على الميراث لتجنب المشكلة.

بالنسبة لي ، الجزء الأكثر فائدة من override هو الحصول على خطأ أثناء إعادة البناء .
في الوقت الحالي ، من السهل جدًا إعادة تسمية طريقة ما في فئة أساسية ونسيان إعادة تسمية تلك التي تتجاوزها.
تذكر استدعاء super ليس بهذه الأهمية. بل إنه من الصحيح عدم الاتصال بها في بعض الحالات.

أعتقد أن هناك سوء فهم حول سبب أهمية _override_. ليس لإجبار شخص ما على استدعاء طريقة _super () _ ، ولكن لجعله يدرك وجود هذا الأسلوب والسماح له بأن يقرر بوعي ما إذا كان يريد قمع سلوكه أو توسيعه.

النمط الذي أستخدمه للتأكد من صحته هو استخدام Parameters و ReturnType أثناء الإشارة بوضوح شديد إلى الفئة الأساسية ، شيء من هذا القبيل:

class Base {
    public methodName(arg1: string, arg2: number): boolean {
        return false; // base behaviour, may be stub.
    }
}
class Derived extends Base {
    public methodName(...args: Parameters<Base["methodName"]>): ReturnType<Base["methodName"]> {
        const [meaningful, variableNames] = args;
        return true; // implemented behaviour here.
    }
}

هذا النوع من الأنماط هو الأساس لاقتراحي إضافة كلمة رئيسية inherit . . هذا يضمن أن أي تحديثات للفئة الأساسية تنتشر تلقائيًا وإذا كانت غير صالحة في الفئة المشتقة ، فسيتم تقديم خطأ ، أو تغيير اسم الفئة الأساسية ، يتم أيضًا تقديم خطأ ، أيضًا مع فكرة inherit لست مقيدًا بتقديم توقيع متطابق للفئة الأساسية فقط ولكن يمكنك تمديدها بشكل حدسي.

أعتقد أيضًا أن هناك سوء فهم حول سبب أهمية override ، لكنني لا أفهم حقًا كل هذه المشكلات مع "ما إذا كانت هناك حاجة إلى super() ".

لتكرار ما قاله lorenzodallavecchia والمؤلف الأصلي للمسألة ، فإن وضع علامة على الوظيفة على أنها override هي آلية لتجنب الأخطاء التي تحدث عند إعادة بناء الطبقة الفائقة ، وتحديدًا عند اسم و / أو توقيع تتغير الوظيفة التي تم تجاوزها أو تتم إزالة الوظيفة.

ضع في اعتبارك أن الشخص الذي يغير توقيع الوظيفة المتجاوزة قد لا يكون على دراية بوجود تجاوزات. إذا (على سبيل المثال في C ++) لم يتم تمييز التجاوزات بشكل صريح على أنها تجاوزات ، فإن تغيير اسم / توقيع الوظيفة التي تم تجاوزها لا يؤدي إلى حدوث خطأ في الترجمة ، يؤدي ببساطة إلى عدم تجاوز هذه التجاوزات. (و .. ربما لم يعد مفيدًا ، ولم يعد يتم استدعاؤه بواسطة الكود المستخدم للاتصال بهم ، وإدخال مجموعة من الأخطاء الجديدة لأن الأشياء التي من المفترض أن تستدعي التجاوزات تستدعي الآن التنفيذ الأساسي)

تحافظ الكلمة الأساسية للتجاوز ، كما هو مطبق في C ++ ، على الشخص الذي يغير التنفيذ الأساسي من هذه المشاكل ، لأنه فورًا بعد إعادة البناء ، ستشير أخطاء التجميع إلى وجود مجموعة من التجاوزات (التي تتجاوز تطبيقًا أساسيًا غير موجود الآن) ودليل لهم في حقيقة أنهم ربما يحتاجون إلى إعادة تشكيل التجاوزات أيضًا.

هناك فوائد ثانوية لقابلية الاستخدام لـ IDE لامتلاك معدل override أيضًا (مذكور أيضًا من قبل المؤلف الأصلي) ، أي أنه عند كتابة الكلمة override ، يمكن لـ IDE أن يظهر لك مجموعة من الاحتمالات يمكن تجاوزها.

جنبًا إلى جنب مع الكلمة الرئيسية override ، سيكون من الجيد أيضًا تقديم علامة ، عند تمكينها ، تتطلب أن تستخدم جميع الطرق التي تتجاوز أخرى الكلمة الرئيسية override ، وذلك لتجنب الحالات التي تنشئ فيها طريقة في فئة فرعية وفي المستقبل ، تنشئ الفئة الأساسية (التي يمكن أن تكون في مكتبة تابعة لجهة خارجية) طريقة بنفس اسم الطريقة التي أنشأتها في الفئة الفرعية (والتي قد تسبب أخطاء الآن ، لأن الفئة الفرعية قد تجاوزت هذه الطريقة ).

الحصول على خطأ تجميع في هذه الحالة ، قائلًا إنك بحاجة إلى إضافة الكلمة الرئيسية override في هذه الطريقة (حتى لو كان صعبًا ، قد لا تتجاوز الطريقة فعليًا ، فقط قم بتغيير اسمها حتى لا تتجاوز الطريقة التي تم إنشاؤها حديثًا في الفئة الأساسية) أفضل بكثير وتجنب أخطاء وقت التشغيل المحتملة.

من واقع خبرتي في كتابة Java ، فإن Override شائع جدًا لدرجة أنه يصبح ضجيجًا.

بعد عدة سنوات جافا أعارض بشدة البيان أعلاه. التجاوز هو وسيلة اتصال ، مثل الاستثناءات المحددة. لا يتعلق الأمر بالإعجاب أو الكراهية ، بل يتعلق بالجودة والتوقعات.

وبالتالي فإن +1 كبيرة لـ lucasbasquerotto ، لكن من وجهة نظر أخرى: إذا قدمت طريقة في فئة فرعية ، فأنا أريد الحصول على دلالة واضحة للتجاوز أو عدم التجاوز. على سبيل المثال ، إذا كنت أرغب في تجاوز طريقة ، فأنا أرغب في الحصول على طريقة لمعرفة ذلك بشكل صريح. إذا كان هناك خطأ ما في تطبيقي ، على سبيل المثال خطأ إملائي أو نسخ ولصق خاطئ ، فأنا أرغب في الحصول على تعليقات حول هذا الموضوع. أو بطريقة أخرى: إذا لم أتوقع التجاوز ، فأنا أرغب في الحصول على تعليقات أيضًا ، إذا حدث التجاوز في بعض الأحيان.

تعليقات من مطور Java آخر ... override هي الميزة الوحيدة التي أفتقدها حقًا من Typescript.

لدي 15 خبرة في Java و 1-2 سنوات أو Typescript مريحة جدًا مع كليهما.

zen ofoverride هو أنه يمكنك إزالة طريقة من واجهة وسيتوقف التجميع.

إنه مثل عكس الربط المحكم للطرق الجديدة. يسمح لك بإزالة القديمة.

إذا قمت بتنفيذ واجهة وإضافة طريقة ، فستفشل عملية التجميع ولكن لا يوجد معكوس لذلك.

إذا قمت بإزالة طريقة فسوف ينتهي بك الأمر مع رمز ميت.

(على الرغم من أنه يمكن إصلاح ذلك بواسطة لينتر)

لكي أكون واضحًا ، عندما قلت أن @Override كان ضجيجًا ، كنت أشير على وجه التحديد إلى التعليق حول أنه إشارة إلى أنك بحاجة إلى الاتصال بـ super . يعتبر الفحص الذي يوفره أمرًا ذا قيمة ، ولكن بالنسبة لأي طريقة تم تجاوزها ، فهي عبارة عن إهمال كامل سواء كان مطلوبًا أو لا طائل من الاتصال بـ super . نظرًا لأن مثل هذا الكسر الكبير من الطرق التي تم تجاوزها _لا ينبغي __ ، فهي غير فعالة لهذا الغرض.

نقطتي الكاملة هي أنه إذا كنت تريد التأكد من إعادة استدعاء الفئات الفرعية إلى طرقك القابلة للتجاوز ، فإن الطريقة الفعالة الوحيدة للقيام بذلك هي جعل الطريقة final (انظر # 33446 ، من بين أمور أخرى) وجعلها تستدعي _differently-named_ طريقة القالب الفارغة التي يمكن تجاوزها بأمان _ بدون _ استدعاء super . ببساطة لا توجد طريقة معقولة أخرى للحصول على هذا الثابت. أنا أؤيد تمامًا override ، ولكن بصفتي شخصًا تم حرقه من قبل المستخدمين الذين قاموا بشكل غير صحيح بتصنيف واجهات برمجة التطبيقات (APIs) الخاصة بي (وأنا على الخطاف لعدم كسرها) ، أعتقد أنه من الجدير تحويل الانتباه إلى final هو الحل الصحيح لهذه المشكلة ، بدلاً من override كما تم اقتراحه.

أنا في حيرة من أمري حول سبب استمرار الناس في اقتراح واحد من final أو override على الآخر. بالتأكيد نريد كلاهما ، لأنهما يحلان مشاكل مختلفة.

تجاوز
يمنع الأشخاص الذين يقومون بتمديد فصل دراسي من الكتابة بطريق الخطأ فوق وظيفة بوظائفهم ، مما يؤدي إلى كسر الأشياء في هذه العملية دون معرفة ذلك. يتيح وجود كلمة رئيسية تجاوز للمطورين معرفة أنهم في الواقع يتخطون وظيفة موجودة ، ومن ثم يمكنهم اختيار إما إعادة تسمية وظيفتهم أو استخدام التجاوز (وبعد ذلك يمكنهم معرفة ما إذا كانوا بحاجة إلى استدعاء super).

أخير
امنع الأشخاص الذين يقومون بتمديد فصل دراسي من الكتابة فوق وظيفة بشكل كامل ، بحيث يضمن المنشئ الأصلي للفصل التحكم الكامل.

@ sam-s4s نريد التعريف أيضًا :-)

مثال على القضية ..

مبدئي

class Base {}
class Entity extends Base {
    id() {
        return 'BUG-123' // busisess entity id
    }
}

الفئة الأساسية معاد بناؤها

class Base {
    id() {
        return '84256635572' // storage object id
    }
}
class Entity extends Base {
    id() {
        return '12' // busisess entity id
    }
}

لدينا زيادة عرضية هنا.

حالة مع الكلمة الرئيسية define

class Base {
    define id() {
        return '84256635572' // storage object id
    }
}
class Entity extends Base {
    define id() {
        return '12' // busisess entity id
    }
}

يجب أن يكون خطأ: إعادة تحديد عرضي.

الحل البديل مع الديكور

@ nin-jin ليس define مثل _not_ استخدام override ؟

@ sam-s4s نريد التعريف أيضًا :-)

أوه ، لم أر أي شخص هنا يذكر كلمة رئيسية define - ولكن مما تصفه ، أفترض أنك تقصد مثل كلمة virtual في C #؟

(كما أنني وجدت لك مثالًا محيرًا بعض الشيء ، حيث إن فصلك الدراسي Base و Entity غير مرتبطين. هل تقصد أن Entity يمدد Base ؟)

أعتقد أن هناك سيناريوهين ...
أ) تكتب صنفك الأساسي ، بافتراض أنه يمكن تجاوز كل شيء بدون final .
ب) تكتب صنفك الأساسي بافتراض أن كل شيء بدون virtual لا يمكن تجاوزه.

@ sam-s4s يمتد الكيان إلى القاعدة ، بالطبع. :-) لقد أصلحت رسالتي. virtual يدور حول شيء آخر.
lorenzodallavecchia عدم استخدام override هو define | override للمترجم. استخدام define هو define فقط ويمكن للمجمع أن يتحقق بدقة من ذلك.

@ nin-jin في نموذجك ، هذا يعني أن عدم استخدام الكلمة الرئيسية override لا يزال قانونيًا ، أليس كذلك؟ على سبيل المثال:

class Base {
  myMethod () { ... }
}

class Overridden extends Base {
  // this would not fail because this is interpreted as define | override.
  myMethod () { ... }
}

من الناحية المثالية ، ينتج عن المثال أعلاه خطأ ما لم تستخدم الكلمة الأساسية override . على الرغم من ذلك ، أتخيل أنه سيكون هناك علامة مترجم لإلغاء الاشتراك في التحقق من التجاوز ، حيث يكون إلغاء الاشتراك هو نفس الشيء مثل override | define لجميع الطرق

bioball نعم ، إنه مطلوب للتوافق مع الكثير من التعليمات البرمجية الموجودة.

@ sam-s4s يمتد الكيان إلى القاعدة ، بالطبع. :-) لقد أصلحت رسالتي. virtual يدور حول شيء آخر.

ما زلت غير متأكد مما تقصده بالتعريف ... هذا هو تعريف virtual في C #:

The virtual keyword is used to modify a method, property, indexer, or event declaration and allow for it to be overridden in a derived class.

ربما تقصد العكس ، حيث ، بدلاً من الحاجة إلى تحديد الوظيفة كـ virtual من أجل التجاوز ، يمكنك وضع علامة على الوظيفة كـ define مما يعني أنه يجب عليك استخدام override الكلمة الرئيسية لتجاوز ذلك؟

(لماذا define ؟ لم أر هذه الكلمة الرئيسية بلغة أخرى)

لقد بحثت في هذا الموضوع في بضع دقائق ، لكنني لم أر أي شخص يقترح استخدام التجاوز كطريقة لتجنب تكرار تحديد المعلمات وقيم الإرجاع. علي سبيل المثال:

class Animal {
    move(meters:number):void {
    }
}

class Snake extends Animal {
    override move(meters) {
    }
}

في المثال أعلاه ، سيكون لدى move نفس نوع الإرجاع المطلوب ( void ) و meters سيكون له نفس النوع أيضًا ، لكن تحديده لن يكون ضروريًا. تحاول الشركة الكبيرة التي أعمل بها ترحيل جميع ملفات جافا سكريبت الخاصة بنا إلى نص مكتوب ، بعيدًا عن كتابة مترجم Closure. ومع ذلك ، أثناء وجودك في Closure ، يمكننا فقط استخدام @override وسيتم استنتاج جميع الأنواع من الطبقة الفائقة. هذا مفيد جدًا في تقليل احتمالية عدم التطابق وتقليل الازدواجية. يمكن للمرء حتى أن يتخيل تنفيذ هذا بحيث لا يزال من الممكن تمديد الطريقة بمعلمات إضافية ، حتى أثناء عدم تحديد أنواع للمعلمات المحددة في الطبقة الفائقة. علي سبيل المثال:

class Animal {
    move(meters:number):number {
    }
}

class Snake extends Animal {
    override move(meters, time:number) {
    }
}

الدافع بالنسبة لي المجيء إلى هنا وكتابة تعليق هو أن شركتنا تطلب الآن تحديد جميع الأنواع حتى للطرق التي تم تجاوزها ، لأن الكتابة المطبوعة لا تحتوي على هذه الوظيفة (ونحن في عملية ترحيل طويلة). مزعج جدا.

FWIW ، في حين أنه من الأسهل الكتابة ، فإن حذف أنواع المعلمات (ومثيلات أخرى من استدلال نوع الملف المتقاطع) يعيق قابلية القراءة ، نظرًا لأنه يتطلب من شخص غير مألوف بالشفرة أن يبحث عن مكان تعريف الطبقة الفائقة من أجل معرفة النوع meters هو (بافتراض أنك تقرأه خارج IDE ، وهو أمر شائع جدًا للمشاريع غير المألوفة حيث لا يوجد لديك IDE تم إعداده حتى الآن). ويتم قراءة الكود أكثر بكثير مما هو مكتوب.

shicks يمكنك قول ذلك عن أي متغير أو فئة مستوردة. إن تكرار كل المعلومات التي قد تحتاجها في ملف واحد يلغي الغرض من التجريد والنمطية. إجبار الأنواع على التكرار ينتهك مبدأ الجفاف.

هل كانت هذه الصفحة مفيدة؟
0 / 5 - 0 التقييمات

القضايا ذات الصلة

jbondc picture jbondc  ·  3تعليقات

weswigham picture weswigham  ·  3تعليقات

siddjain picture siddjain  ·  3تعليقات

MartynasZilinskas picture MartynasZilinskas  ·  3تعليقات

dlaberge picture dlaberge  ·  3تعليقات