2017-01-25 24 views
0

我正在處理抽象父類需要繼承通過其子類提供的泛型類型的所有屬性的模式。如何從TypeScript中的通用參數擴展?

下面就來看看在構造函數基類,它提供了一個基本的想法,也可以是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 { 
... 

不幸的是,這似乎並沒有得到支持當前等我不知所措,我怎麼可以讓我的基類擴展它的泛型參數,使得從它繼承also receives the properties of the generic type it's encapsulating任何類:

export class AuthorFact extends Base<Author> { 

    public get RowKey() { return (this as any).id } 
} 

正如你所看到的,我被迫從我的基類型中刪除& T,並使用(this as any)來抑制編譯器拋出錯誤。

我最終希望.id上的類型檢查能夠成功,以及Author的任何其他屬性都可以在我創建的實例上使用。

回答

1

即使最新提交的是allows classes and interfaces to derive from object types and intersections of object types,從泛型類型參數擴展類仍是不允許的:

接口或類不能因爲它 不可能始終如一地核實有延長裸類型參數該類型的實例化中沒有成員名稱 衝突。

但現在被允許的是瘋狂的:你可以在運行時建立類並讓它們進行類型檢查。 (這需要npm i [email protected]目前是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 

事實證明,你不能做到這一點與抽象類,但我認爲結構類型仍然讓你做你想要的這裏。

+0

有沒有可能我可以使用映射類型?另外,爲什麼你使用函數而不是類來包裝一些東西? –

+1

不知道映射類型。函數是必需的,因爲你不能'type T = A & B; class C extends T {}',因爲對於'extends',T必須是一個值和一個類型 - 你會得到'錯誤TS2693:'T'指的是一種類型,但在這裏被用作一個值。返回類類型的函數是唯一的方法。 – artem

+0

我會記住你的答案,因爲它似乎是一種可行的方法。我只想驗證新語言功能尚未實現,可能會產生更少的...迂迴...解決方案;) –