ค้นหาเว็บไซต์

typescript - ชนิดข้อมูลที่อนุญาตชื่อของฟิลด์และคุณสมบัติของตัวมันเองและคลาสที่สืบทอด


สมมติว่าเรามีคลาสฐานซึ่งควรประกาศเมธอดที่รับชื่อของหนึ่งในคุณสมบัติของคลาสหรือชื่อของคุณสมบัติของคลาสที่สืบทอดมาใด ๆ เป็นพารามิเตอร์แบบสตริง:

export abstract class BaseClass {
    public someField1: number = 2;

    public get someProperty1(): number {
        return 1;
    }

    public someMethod1(): void {
    }

    // TODO: What is the proper type for propertyName?
    protected method(propertyName: string): void {
        const propertyValue: any = this[propertyName];
        // ...
    }
}

และเรามีคลาสย่อยมาบางคลาสพร้อมการทดสอบของ method():

export class DerivedClass extends BaseClass {
    protected someField2: number = 2;

    protected get someProperty2(): number {
        return 1;
    }

    protected someMethod2(): void {
    }

    public test(): void {
        super.method("someField1"); // Allowed
        super.method("someProperty1"); // Allowed
        super.method("someMethod1"); // Not allowed

        super.method("someField2"); // Allowed
        super.method("someProperty2"); // Allowed
        super.method("someMethod2"); // Not allowed

        super.method(""); // Not allowed
        super.method("qwerty"); // Not allowed
    }
}

นี่คือสนามเด็กเล่น

ในตัวอย่างด้านบน ผมได้ใช้ string เป็นประเภทของพารามิเตอร์ของ method() แต่ในกรณีนี้ สตริงใด ๆ ก็สามารถถูกส่งเข้าไปได้ และตัวคอมไพเลอร์ไม่สามารถตรวจสอบได้ว่านี่คือชื่อของคุณสมบัติที่มีอยู่จริงหรือไม่

นอกจากนี้ จะดีถ้าไม่รวมชื่อเมธอด ("someMethod1", "someMethod2") จากรายการค่าที่อนุญาต

ประเภทใดที่ propertyName ควรมีเพื่ออนุญาตเฉพาะชื่อของฟิลด์และคุณสมบัติของ BaseClass และคลาสที่สืบทอดใด ๆ?

คำตอบ #1

คุณสามารถเกือบจะทำได้ตามที่คุณต้องการ คุณสามารถใช้ประเภท this แบบ Polymorphic เพื่ออ้างอิงถึงคลาสปัจจุบัน (ดังนั้นมันจะเป็นตัวแทนของคลาสที่สืบทอดในคลาสที่สืบทอดใหม่และคลาสพื้นฐานในคลาสพื้นฐาน) เรายังสามารถใช้ keyof เพื่อรับคีย์ของประเภทหนึ่ง (ในกรณีนี้คือประเภท this)

มีข้อจำกัดสองประการ:

  • keyof ไม่คืนค่าคุณสมบัติส่วนตัวของประเภท ซึ่งจะทำงานได้เฉพาะกับคุณสมบัติสาธารณะเท่านั้น
  • keyof จะส่งคืนคุณสมบัติทั้งหมด แม้ว่าจะสามารถกรองฟังก์ชันออกได้ แต่สิ่งนี้จะใช้ไม่ได้ภายในคลาส (เนื่องจาก this เป็นพื้นฐานของพารามิเตอร์ชนิดที่เป็นอิสระ มันจึงไม่ทราบอย่างสมบูรณ์ เนื่องจากมันสามารถเป็นคลาสนี้หรือคลาสที่สืบทอดมา และเราจำเป็นต้องใช้ชนิดมีเงื่อนไขในการกรองซึ่งไม่สามารถแก้ไขได้ เว้นแต่ชนิดจะทราบอย่างสมบูรณ์

โค้ด:

export abstract class BaseClass {
    public someField1: number = 2;

    public get someProperty1(): number {
        return 1;
    }

    public someMethod1(): void {
    }

    // TODO: What is the proper type for propertyName?
    protected method(propertyName: keyof this): void {
        const propertyValue: any = this[propertyName];
        // ...
    }
}

export class DerivedClass extends BaseClass {
    private someField2: number = 2;

    public get someProperty2(): number {
        return 1;
    }

    public someMethod2(): void {
    }

    public test(): void {
        super.method("someField1"); // Allowed
        super.method("someProperty1"); // Allowed
        super.method("someMethod1"); // Not allowed

        super.method("someField2"); // Allowed
        super.method("someProperty2"); // Allowed
        super.method("someMethod2"); // allowed

        super.method(""); // Not allowed
        super.method("qwerty"); // Not allowed
    }
}

เวอร์ชันที่กรองฟังก์ชันออก แต่ใช้ได้เฉพาะจากภายนอกคลาสจะใช้ประเภทตามเงื่อนไข:

type FilterFucntion<T> = { [P in keyof T]-?: T[P] extends Function ? never : P }[keyof T]
export abstract class BaseClass {
    public someField1: number = 2;

    public get someProperty1(): number {
        return 1;
    }

    public someMethod1(): void {
    }

    public method(propertyName: FilterFucntion<this>): void {
        const propertyValue: any = this[propertyName];
        // ...
    }
}

export class DerivedClass extends BaseClass {
    public someField2: number = 2;

    public get someProperty2(): number {
        return 1;
    }

    public someMethod2(): void {
    }
}
function test(): void {
    const o = new DerivedClass()
    o.method("someField1"); // Allowed
    o.method("someProperty1"); // Allowed
    o.method("someMethod1"); // Not allowed

    o.method("someField2"); // Allowed
    o.method("someProperty2"); // Allowed
    o.method("someMethod2"); // Not allowed

    o.method(""); // Not allowed
    o.method("qwerty"); // Not allowed
}