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
}
