Generics 如何从TypeScript中的泛型参数进行扩展?
我正在研究一种模式,其中抽象父类需要继承通过其子类提供的泛型类型的所有属性 下面是基类的构造函数,它提供了一个基本概念,也可以是:Generics 如何从TypeScript中的泛型参数进行扩展?,generics,inheritance,typescript,Generics,Inheritance,Typescript,我正在研究一种模式,其中抽象父类需要继承通过其子类提供的泛型类型的所有属性 下面是基类的构造函数,它提供了一个基本概念,也可以是: 我想要的是一种有效的表达方式: 导出抽象类基类实现Storage.AbstractEntity&T{ ... 不幸的是,目前似乎不支持这一点,因此我不知道如何使基类扩展其泛型参数,以便任何从基类继承的类: 导出类AuthorFact扩展基{ public get RowKey(){return(此为任意).id} } 如您所见,我被迫从基类型中删除&T,并
我想要的是一种有效的表达方式:
导出抽象类基类实现Storage.AbstractEntity&T{
...
不幸的是,目前似乎不支持这一点,因此我不知道如何使基类扩展其泛型参数,以便任何从基类继承的类:
导出类AuthorFact扩展基{
public get RowKey(){return(此为任意).id}
}
如您所见,我被迫从基类型中删除
&T
,并使用(此为任意)
来禁止编译器抛出错误
我最终希望
.id
上的类型检查成功,以及Author
的任何其他属性在我正在创建的实例上可用。即使最新的提交仍然不允许从泛型类型参数扩展类:
接口或类无法扩展裸类型参数,因为它
无法一致地验证没有成员名称
类型的实例化中存在冲突
但现在允许的是疯狂的:您可以在运行时构建类并检查它们的类型(这需要npmi)typescript@next
当前为2.2):
从“Azure”导入*为Azure;
从“lodash”中导入*as uu;
导出类作者{
公共id:string;
public firstName:string;
公共姓氏:字符串;
public nativeId:{[key:string]:string}={};
公共职位:职位[];
公众创建:日期;
}
出口级邮政{
公共构造函数(
公共id:string,
公共作者:作者,
公共标题:字符串,
公共内容:字符串,
公共作者:日期
) {
}
}
类型构造函数=new()=>T;
类型DataConstructor=new(数据:data)=>T;
函数基(数据类:构造函数){
类扩展(dataClass作为构造函数){
公共构造函数(数据:T){
超级();
_.assign(此,uu.省略(数据)[
“表”,
“身份证”,
“分区键”,
“RowKey”,
]));
}
[属性:字符串]:字符串|数字|布尔值|日期;
}
返回类作为构造函数;
}
函数事实(超类:DataConstructor){
类扩展(作为DataConstructor的超类){
public get PartitionKey(){return“fact”}
}
作为DataConstructor返回类
}
可识别函数(超类:DataConstructor){
类扩展(作为DataConstructor的超类){
公共id:string;
public get RowKey(){返回this.id}
}
作为DataConstructor返回类
}
函数IdentifiableDataFact(数据类:构造函数){
返回可识别(事实(基本(数据类));
}
类AuthorFact扩展了IdentifiableDataFact(Author){
}
//让我们初始化一些数据
让作者=新作者();
author.id='a';
author.firstName='z';
author.lastName='q';
//让我们看看我们有什么
让authorFact=newauthorFact(author);//它有一个接受author的构造函数
让e:Azure.Entity=authorFact;//它在结构上与Azure.Entity兼容
//它有PartitionKey
console.log(authorFact.PartitionKey);//打印事实
//它有一些被基构造函数复制的属性(id除外)
console.log(authorFact.firstName);//打印z
//它具有索引签名(使用--noImplicitAny进行检查)
常量ps=['lastName','nativeIds'];
ps.forEach(p=>console.log(authorFact[p]);//打印q{}
//它有RowKey,但这里没有定义(这可能不是您想要的)
//因为在基构造函数中复制时显式省略了id
console.log(authorFact.RowKey);//未定义
事实证明,抽象类无法做到这一点,但我认为结构类型仍然允许您在这里做您想做的事情。我是否有可能使用映射类型?还有,为什么您要使用函数而不是类来包装某些东西?不知道映射类型。函数是必要的,因为您不能执行
typeT=A&B;C类扩展了T{}
因为对于扩展
,T必须既是一个值又是一个类型-您会得到错误TS2693:“T”仅指一个类型,但在这里被用作一个值
。返回类类型的函数是唯一的方法。我将记住您的答案,因为它似乎是一种可行的方法。我只想验证一下语言特性还没有出现,可能会产生一个不那么……迂回……的解决方案;)
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 }
}
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