[英]How do I extend from a generic parameter in TypeScript?
我正在研究一種模式,其中抽象父類需要繼承通過其子類提供的泛型類型的所有屬性。
這是基類的構造函數,它給出了基本概念,也可以在github上查看 :
public constructor(data: T) {
_.assign(this, _.omit(data, [
"table",
"id",
"PartitionKey",
"RowKey",
]));
}
我正在尋找某種有效表達這一點的方式 :
export abstract class Base<T extends EntityData> implements Storage.AbstractEntity & T {
...
不幸的是,目前似乎尚不支持此方法,因此我對如何使我的基類擴展其泛型參數感到困惑,因為任何繼承自該類的類也都將獲得其封裝的泛型類型的屬性。 :
export class AuthorFact extends Base<Author> {
public get RowKey() { return (this as any).id }
}
如您所見,我被迫從我的基本類型中刪除& T
並使用(this as any)
禁止編譯器拋出錯誤。
我最終想要的是使.id
上的類型檢查成功,以及在我創建的實例上可以使用Author
任何其他屬性。
即使使用允許類和接口從對象類型和對象類型的交集派生的最新提交,仍不允許從通用類型參數擴展類:
接口或類無法擴展裸類型參數,因為無法一致地驗證類型實例化中是否沒有成員名稱沖突。
但是現在所允許的是瘋狂的:您可以在運行時構建類並進行類型檢查。 (這需要npm i typescript@next
當前為2.2):
import * as Azure from "azure";
import * as _ from "lodash";
export class Author {
public id: string;
public firstName: string;
public lastName: string;
public nativeIds: {[key: string]: string} = {};
public posts: Post[];
public created: Date;
}
export class Post {
public constructor(
public id: string,
public author: Author,
public title: string,
public content: string,
public authored: Date
) {
}
}
type Constructor<T> = new () => T;
type DataConstructor<T, Data> = new (data: Data) => T;
function Base<T>(dataClass: Constructor<T>) {
class Class extends (dataClass as Constructor<{}>) {
public constructor(data: T) {
super();
_.assign(this, _.omit(data, [
"table",
"id",
"PartitionKey",
"RowKey",
]));
}
[property: string]: string | number | boolean | Date;
}
return Class as Constructor<T & Class>;
}
function Fact<T, Data>(superClass: DataConstructor<T, Data>) {
class Class extends (superClass as DataConstructor<{}, Data>) {
public get PartitionKey() { return "fact" }
}
return Class as DataConstructor<T & Class, Data>
}
function Identifiable<T, Data>(superClass: DataConstructor<T, Data>) {
class Class extends (superClass as DataConstructor<{}, Data>) {
public id: string;
public get RowKey() { return this.id }
}
return Class as DataConstructor<T & Class, Data>
}
function IdentifiableDataFact<Data>(dataClass: Constructor<Data>) {
return Identifiable(Fact(Base(dataClass)));
}
class AuthorFact extends IdentifiableDataFact(Author) {
}
// let's init some data
let author = new Author();
author.id = 'a';
author.firstName = 'z';
author.lastName = 'q';
// let's see what we've got
let authorFact = new AuthorFact(author); // it has a constructor that takes Author
let e: Azure.Entity = authorFact; // it's structurally compatible with Azure.Entity
// it has PartitionKey
console.log(authorFact.PartitionKey); // prints fact
// it has some properties that were copied over by Base constructor (except id)
console.log(authorFact.firstName); // prints z
// it has index signature (check with --noImplicitAny)
const ps = ['lastName', 'nativeIds'];
ps.forEach(p => console.log(authorFact[p])); // prints q {}
// and it has RowKey but it's undefined here (this might not be what you want)
// because id is explicitly omitted from being copied in Base constructor
console.log(authorFact.RowKey); // undefined
事實證明,您不能使用抽象類來做到這一點,但我認為結構類型仍然可以讓您在這里做您想做的事情。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.