我正在研究一种模式,其中抽象父类需要继承通过其子类提供的泛型类型的所有属性。
这里看一下基类的构造函数,它提供了一个基本的想法,也可以是viewed on github:
public constructor(data: T) {
_.assign(this, _.omit(data, [
"table",
"id",
"PartitionKey",
"RowKey",
]));
}
我正在寻找的是一种有效表达this的方式:
export abstract class Base<T extends EntityData> implements Storage.AbstractEntity & T {
...
不幸的是,目前似乎并没有支持这一点,所以我不知道如何让我的基类扩展其泛型参数,以便任何继承它的类{{3 }}:
export class AuthorFact extends Base<Author> {
public get RowKey() { return (this as any).id }
}
正如您所看到的,我被迫从我的基本类型中删除& T
并使用(this as any)
来禁止编译器抛出错误。
我最终希望.id
上的类型检查成功,以及{I}创建的实例上的Author
的任何其他属性。
答案 0 :(得分:2)
即使使用allows classes and interfaces to derive from object types and intersections of object types的最新提交,仍然不允许从泛型类型参数扩展类:
接口或类不能扩展裸类型参数,因为它 无法一致地验证没有成员名称 类型实例化中的冲突。
但是现在允许的是疯狂的:你可以在运行时构建类并对它们进行类型检查。 (这需要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
事实证明,你不能用抽象类做到这一点,但我认为结构类型仍然允许你在这里做你想做的事。