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