angular - เจนเนอริกของ Typescript ในคอลแบ็ก
ฉันกำลังพยายามสร้างบริการทั่วไปที่จะดึงข้อมูลระยะไกลมาบางส่วนและสร้างวัตถุบางอย่างจากมัน
@Injectable()
export class tService<T> {
private _data: BehaviorSubject<T[]> = new BehaviorSubject([]);
private url = '...url...'; // URL to web api
constructor(private http: HttpClient) {
this.http.get<T[]>(this.url).subscribe(theThings => {
theThings = _.map(theThings, (theThing) => new T(theThing));
this._data.next(theThings);
});
}
}
นี่ทำให้ฉันได้รับข้อผิดพลาด T หมายถึงประเภทเท่านั้น แต่ถูกใช้เป็นค่าที่นี่ โอเค ไม่เป็นไร ฉันเข้าใจสิ่งที่เกิดขึ้นแล้ว ฉันเคยเห็นคำถามบางข้อที่ถามเกี่ยวกับสิ่งที่คล้ายกัน
ตัวอย่างเช่น:
- วิธีสร้างออบเจ็กต์ใหม่จากพารามิเตอร์ชนิดในคลาสเจนริกในไทพ์สคริปต์?
- TypeScript คืนอ็อบเจ็กต์ที่สร้างจากเจนเนอริก
- รับตัวสร้าง/อินสแตนซ์จากประเภททั่วไปใน TypeScript
ดูเหมือนว่าวิธีแก้ปัญหาใด ๆ ที่ฉันเจอมาจะเป็นการกำหนดคลาสแบบคงที่ในบางจุด หรือเพิ่มตัวสร้างคลาสของ T ที่ไหนสักแห่ง แต่ฉันไม่สามารถหาวิธีทำได้ ปัญหาของฉันคือคลาสที่ถูกสร้างขึ้นมีพารามิเตอร์ในตัวสร้าง และฉันกำลังใช้ระบบ DI ของ Angular ดังนั้นฉันจึงไม่สามารถ (???) เพิ่มพารามิเตอร์ไปยังตัวสร้างของบริการได้
constructor(private playersService: gService<Player>, private alliesService: gService<Ally>) { }
มีใครสามารถคิดออกไหมว่าฉันควรทำอะไรที่นี่? นอกจากนี้ ฉันขอแนะนำให้คำตอบไม่ใช่บางอย่างที่เป็น 'แฮ็ก' ถ้าเป็นไปได้ ถ้าเป็นการเลือกระหว่างการทำบางอย่างที่อ่านแทบไม่ออกกับการคัดลอกและวางบริการนั้นหลายครั้งแล้วเปลี่ยนว่ามันอ้างถึงคลาสอะไร ฉันจะเลือกตัวเลือกที่สอง
คำตอบ #1
ชื่อคลาสหมายถึงทั้งประเภทของคลาส แต่ยังหมายถึงคอนสตรัคเตอร์ของมันด้วย นั่นคือเหตุผลที่คุณสามารถเขียนได้ทั้ง let x: ClassName และ new ClassName() พารามิเตอร์เจนเนอริกเป็นเพียงประเภทเท่านั้น (คล้ายกับอินเทอร์เฟซตัวอย่างเช่น) นั่นคือเหตุผลที่คอมไพเลอร์แจ้งข้อผิดพลาดว่าคุณกำลังใช้มันเป็นค่า (ค่าที่คาดหวังคือฟังก์ชันคอนสตรัคเตอร์) สิ่งที่คุณต้องทำคือส่งพารามิเตอร์เพิ่มเติมซึ่งจะเป็นคอนสตรัคเตอร์สำหรับ T
export class tService<T> {
private _data: BehaviorSubject<T[]> = new BehaviorSubject<T>([]);
private url = '...url...'; // URL to web api
constructor(private http: HttpClient, ctor: new (data: Partial<T>)=> T) {
this.http.get<T[]>(this.url).subscribe(theThings => {
theThings = _.map(theThings, (theThing) => new ctor(theThing));
this._data.next(theThings);
});
}
}
//Usage
class MyClass {
constructor(p: Partial<MyClass>) {
// ...
}
}
new tService(http, MyClass)
หมายเหตุ พารามิเตอร์สำหรับลายเซ็นต์ของคอนสตรัคเตอร์อาจแตกต่างกันไปตามกรณีการใช้งานของคุณ
แก้ไข
คุณได้กล่าวว่าคุณไม่สามารถเพิ่มอาร์กิวเมนต์ให้กับคอนสตรัคเตอร์ได้ generics (และทุกประเภทโดยทั่วไป) จะถูกลบออกเมื่อรันโค้ด ดังนั้นคุณจึงไม่สามารถพึ่งพา T ในเวลารันไทม์ได้ ที่ใดสักแห่ง จะต้องมีคนส่งคอนสตรัคเตอร์ของคลาสเข้ามา คุณสามารถทำได้หลายวิธี วิธีที่ง่ายที่สุดคือสร้างคลาสเฉพาะสำหรับแต่ละอินสแตนซ์ของ T แล้วจึงฉีดคลาสที่เฉพาะนั้น:
class tServiceForMyClass extends tService<MyClass> {
constructor(http: HttpClient) {
super(http, MyClass)
}
}
หรือคุณสามารถทำงานของคุณขึ้นอยู่กับคอนสตรัคเตอร์ ในเมธอด init ที่ต้องการคอนสตรัคเตอร์เป็นพารามิเตอร์:
export class tService<T> {
private _data: BehaviorSubject<T[]> = new BehaviorSubject<T>();
private url = '...url...'; // URL to web api
constructor(private http: HttpClient){}
init(ctor: new (data: Partial<T>)=> T) {
this.http.get<T[]>(this.url).subscribe(theThings => {
theThings = _.map(theThings, (theThing) => new ctor(theThing));
this._data.next(theThings);
});
}
}
