Một câu hỏi cũ với hầu hết các câu trả lời đúng, nhưng không hiệu quả. Đây là những gì tôi đề xuất:
Tạo một lớp cơ sở chứa phương thức init () và các phương thức truyền tĩnh (cho một đối tượng và một mảng). Các phương thức tĩnh có thể ở bất cứ đâu; phiên bản với lớp cơ sở và init () cho phép mở rộng dễ dàng sau đó.
export class ContentItem {
// parameters: doc - plain JS object, proto - class we want to cast to (subclass of ContentItem)
static castAs<T extends ContentItem>(doc: T, proto: typeof ContentItem): T {
// if we already have the correct class skip the cast
if (doc instanceof proto) { return doc; }
// create a new object (create), and copy over all properties (assign)
const d: T = Object.create(proto.prototype);
Object.assign(d, doc);
// reason to extend the base class - we want to be able to call init() after cast
d.init();
return d;
}
// another method casts an array
static castAllAs<T extends ContentItem>(docs: T[], proto: typeof ContentItem): T[] {
return docs.map(d => ContentItem.castAs(d, proto));
}
init() { }
}
Cơ học tương tự (với gán () ) đã được đề cập trong bài đăng @ Adam111p. Chỉ là một cách khác (đầy đủ hơn) để làm điều đó. @Timothy Perez rất quan trọng về gán () , nhưng imho nó hoàn toàn thích hợp ở đây.
Thực hiện một lớp dẫn xuất (thực):
import { ContentItem } from './content-item';
export class SubjectArea extends ContentItem {
id: number;
title: string;
areas: SubjectArea[]; // contains embedded objects
depth: number;
// method will be unavailable unless we use cast
lead(): string {
return '. '.repeat(this.depth);
}
// in case we have embedded objects, call cast on them here
init() {
if (this.areas) {
this.areas = ContentItem.castAllAs(this.areas, SubjectArea);
}
}
}
Bây giờ chúng ta có thể truyền một đối tượng được lấy từ dịch vụ:
const area = ContentItem.castAs<SubjectArea>(docFromREST, SubjectArea);
Tất cả hệ thống phân cấp của các đối tượng SubjectArea sẽ có lớp chính xác.
Một trường hợp sử dụng / ví dụ; tạo một dịch vụ Angular (lớp cơ sở trừu tượng một lần nữa):
export abstract class BaseService<T extends ContentItem> {
BASE_URL = 'http://host:port/';
protected abstract http: Http;
abstract path: string;
abstract subClass: typeof ContentItem;
cast(source: T): T {
return ContentItem.castAs(source, this.subClass);
}
castAll(source: T[]): T[] {
return ContentItem.castAllAs(source, this.subClass);
}
constructor() { }
get(): Promise<T[]> {
const value = this.http.get(`${this.BASE_URL}${this.path}`)
.toPromise()
.then(response => {
const items: T[] = this.castAll(response.json());
return items;
});
return value;
}
}
Việc sử dụng trở nên rất đơn giản; tạo một dịch vụ Khu vực:
@Injectable()
export class SubjectAreaService extends BaseService<SubjectArea> {
path = 'area';
subClass = SubjectArea;
constructor(protected http: Http) { super(); }
}
Phương thức get () của dịch vụ sẽ trả về một Promise của một mảng đã được truyền dưới dạng các đối tượng SubjectArea (toàn bộ phân cấp)
Bây giờ nói, chúng tôi có một lớp khác:
export class OtherItem extends ContentItem {...}
Tạo một dịch vụ lấy dữ liệu và chuyển sang đúng lớp cũng đơn giản như:
@Injectable()
export class OtherItemService extends BaseService<OtherItem> {
path = 'other';
subClass = OtherItem;
constructor(protected http: Http) { super(); }
}