چندشکلی این در تایپاسکریپت


من دارم سعی میکنم آنچه را که فکر میکنم یک مورد استفاده "کتاب درسی" برای this چندشکلی است انجام دهم و این کار نمیکند و واقعاً نمیتوانم این را متوجه شوم. تصور کنید که من یک کلاس پایه abstract دارم که قابل تکثیر است، مثلاً:

abstract class X {
    abstract clone(): this;
}

حال میخواهم یک کلاس پایه پیادهسازی کنم که یک پیادهسازی clone ارائه میدهد:

class Y extends X {
    constructor(private x: number) { super() }
    clone(): this { return new Y(this.x + 1); }
}

وقتی این کار را انجام میدهم، خطایی دریافت میکنم که میگوید Type Y is not assignable to type 'this'. من کاملاً سردرگم شدهام. تمام چیزی که میخواهم در اینجا منتقل کنم، محدودیت نوع است که اگر یک زیرکلاس از X متد clone خود را فراخوانی کند، نوع چیزی که دریافت میکنید دقیقاً با نوع زیرکلاس یکسان خواهد بود. آیا این دقیقاً همان کاری نیست که this چندریختی برای آن طراحی شده است؟ من اینجا چه اشتباهی انجام میدهم؟

در اینجا یک لینک به این کد در محیط بازی TypeScript وجود دارد.

پاسخ شماره ۱

من پاسخ آرتِم را به عنوان پاسخ درست علامت زدهام زیرا به نظر میرسد او درست میگوید که واقعاً نمیتوان این کار را به صورت ۱۰۰٪ ایمن انجام داد.

با این حال، راهی وجود دارد تا کامپایلر به محدودیت نوعی که من میخواستم، عمل کند. بنابراین، تصمیم گرفتم این پاسخ را شامل کنم تا در صورتی که برای دیگران مفید باشد، در دسترس باشد. تنها نقطهضعف رویکرد من این است که کمی ناایمن است، دقیقاً به همان دلایلی که آرتِم اشاره میکند، یعنی اینکه کسی میتواند کلاسهای شما را گسترش دهد بدون اینکه پیادهسازی clone را ارائه کند و شرایطی ایجاد کند که مقدار بازگردانده شده در واقع همان چیزی نباشد که شما ادعا میکنید.

راه حل من این بود که فقط یک تبدیل نوع اضافه کنم. دوباره، این به طور کلی ناایمن است. اما اگر هرگز از این کلاسها ارثبری نکنید، تا جایی که من میتوانم بگویم، خوب کار میکند. بنابراین راه حل من این بود:

class Y extends X {
    constructor(private x: number) { super() }
    clone(): this {
        return new Y(this.x + 1) as this;
    }
}

شما میتوانید یک نسخه کامل که اینجا کامپایل شده است ببینید.

پاسخ شماره ۲

خیر، با وضعیت فعلی، این یک مورد استفاده پشتیبانی شده برای این نوع نیست. یک متد با نوع بازگشتی this باید یک نمونه از کلاس مشتق شده بازگرداند حتی اگر این متد در کلاس مشتق شده بازنویسی نشده باشد - برای نمونهای که این موضوع را توجیه میکند، کد موجود در این پاسخ را ببینید.

به عبارت دیگر، با توجه به اعلام abstract clone(): this، این کد باید معتبر باشد حتی اگر Z خودش clone را بازنویسی نکند، اما پیادهسازی شما از clone در Y آن را نقض میکند.

class Z extends Y {
    f() {
        let c: Z = this.clone();
    }
}

بنابراین به نظر میرسد که یک متد با نوع بازگشتی this همیشه باید return this داشته باشد.

بهروزرسانی: من یک مسئله باز با این مورد استفاده در گیتهاب پیدا کردم که بهعنوان «پذیرش درخواستهای کشش» علامتگذاری شده است. با این حال، مطمئن نیستم که واقعاً قصد دارند از آن پشتیبانی کنند یا برنامه دارند cloneNode را بهنحوی دیگر در کامپایلر خود اصلاح کنند.