ترکیب میکسین جابجایی در تایپاسکریپت


بگو من یک کلاس رمزگذار پایه با متد دارم

encode(obj: {body: Uint8Array}): Uint8Array

و من آن را گسترش میدهم تا انواع مختلف obj.body را با یک یا چند میکسین بپذیرد - یعنی:

function numberMixin(encoder) {
    return class extends encoder {
       encode(obj) {
           if (isNumberObj(obj)) {
              ...
              return super.encode(transformed);
           }
           return super.encode(obj);
        }
    }
}

و این میکسینها را ترکیب کنید

const Encoder = etcMixin(numberMixin(stringMixin(Base)));

آیا راهی وجود دارد که هر میکسین/انکودر را حاشیهنویسی کنیم تا متد encode کلاس حاصل بداند کدام نوعها را میپذیرد؟ میتوانم عملکرد ورودی/خروجی را مشخص کنم اما در این صورت ترتیب میکسینها ثابت میشود و به راحتی قابل گسترش نیست.

یک گزینه این است که از پارامترهای نوع عمومی در هر میکسین استفاده کنم، اما در این صورت باید نوع را در هر مرحله از ترکیب مشخص کنم، که به نظر تکراری/خیلی طولانی میآید.

numberMixin<U, T extends Constructor<IEncoder<U>>>(encoder: T): T & Constructor<IEncoder<Number|U>>;

پاسخ شماره ۱

ما میتوانیم از این واقعیت استفاده کنیم که اشتراک نوعهای تابع همانند یک تابع با بارگذاری بیش از حد در نظر گرفته میشود، بنابراین میتوانیم نوع encode را به صورت BaseClass['encode'] & newOverlaodSignature بسازیم.

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

خبر خوب این است که محل فراخوانی خوب به نظر میرسد و تمامی اضافهبارها موجود هستند:

ویرایش کار امتیاز اضافی از نظرات، همچنین انواع را جمعآوری کنید تا بتوانیم از آنها در میکسینهای دیگر استفاده کنیم. میتوانیم این کار را انجام دهیم اگر یک ویژگی ایستا اضافی به نوع اضافه کنیم که شامل فیلدی برای هر نوع اضافه شده باشد. دلیل اینکه ما به یک فیلد نیاز داریم و نه فقط یک فیلد از نوع، این است که نوع بازگشتی AddedStuff & T خواهد بود، بنابراین این به فیلد نوع منتقل میشود و ما یک اشتراک از تمام فیلدهای type تعریف شده از تمام میکسینها دریافت میکنیم. printEncodedMixin از اطلاعات نوع اضافی استفاده میکند:

class EncoderBase {
    constructor(param: string) {

    }
    encode(obj: { body: Uint8Array }): Uint8Array {
        return obj.body;
    }
    static type: { Uint8Array: Uint8Array }
}
type EncoderType = {
    new (...args: any[]) : { encode: (obj: { body: Uint8Array }) => Uint8Array }
    type: any
}

function numberMixin<T extends EncoderType>(encoder: T) {
    function isNumberObj(obj: any): obj is { body: number } {
        return obj && typeof obj.body === 'number';
    }
    let resultClass = class extends encoder {
        encode!: InstanceType<T>['encode'] & ((obj: { body: number }) => Uint8Array);
        static type : { number : number }
    }
    resultClass.prototype.encode = function (obj: any) {
        if (isNumberObj(obj)) {
            return encoder.prototype.encode(obj);
        }
        return encoder.prototype.encode(obj);
    }

    return resultClass;
}

function stringMixin<T extends EncoderType>(encoder: T) {
    function isStringObj(obj: any): obj is { body: string } {
        return obj && typeof obj.body === 'string';
    }
    let resultClass = class extends encoder {
        encode!: InstanceType<T>['encode'] & ((obj: { body: string }) => Uint8Array);
        static type : { string : string }
    }
    resultClass.prototype.encode = function (obj: any) {
        if (isStringObj(obj)) {
            return encoder.prototype.encode(obj);
        }
        return encoder.prototype.encode(obj);
    }
    return resultClass;
}

function printEncodedMixin<T extends EncoderType>(encoder: T) {
    type Body<T> = T extends any ? {body : T} : never;
    return class extends encoder {
        printEncoded(b: Body<T['type'][keyof T['type']]>) {

        }
    }
}

const Encoder = printEncodedMixin(numberMixin(stringMixin(EncoderBase)));

let d = new Encoder(""); // ctor params still work
d.encode({ body: "" });
d.encode({ body: 0 });
d.encode({ body: {} }); // Error
d.printEncoded({ body: "" }) //  printEncoded({body: string;} | {body: number;} | {body: Uint8Array;}): void