2017-09-14 30 views
1

有人可以解釋爲什麼這不起作用:打字稿:polymorhpic此,超載簽名不是與功能實現兼容

abstract class Model { 
    static deserialize<T extends Model>(this: (new() => T), object: any): T; 
    static deserialize<T extends Model>(ctor: (new() => T), object: any): T { 
     return new ctor(); 
    } 

    static deserializeArray<T extends Model>(this: (new() => T), ...objects: any[]): T[]; 
    static deserializeArray<T extends Model>(ctor: (new() => T), ...objects: any[]): T[] { 
     return objects.map(object => Model.deserialize(ctor, object)); 
    } 
} 

class MyModel extends Model { } 

這將使:

let myModel = MyModel.deserialize({}); 
let myModels = MyModel.deserializeArray({}, {}, {}); 

let myModel = Model.deserialize(MyModel, {}); 
let myModels = Model.deserializeArray(MyModel, {}, {}, {}); 

Typescript 2.5.2抱怨「過載簽名與函數實現不兼容」。

爲什麼需要這兩種形式?

考慮一個REST API返回序列化(JSON)型號:

class MyModelController { 
    get(id: number) { 
     let myModel = ... some db/service call ... 
     return myModel.serialize(); 
    } 
} 

然後一個通用服務(角),請求模型:

@Injectable() 
abstract class HttpService { 
    constructor(private http: Http) { } 

    errorHandler(response) { 
     ... 
    } 

    get<T extends Model>(ModelType: (new() => T), endpoint: string): Observable<T> { 
     return this.http.get(endpoint) 
      // we can't call ModelType.deserialize() here... 
      .map(response => Model.deserialize(ModelType, response.json())) 
      .catch(response => this.errorHandler(response)); 
    } 
} 

@Injectable() 
class MyModelService extends HttpService { 
    get(id: number) { 
     return super.get(MyModel, `/api/models/${id}`); 
    } 
} 

解決方案

abstract class Model { 
    static deserialize<T extends Model>(this: (new() => T), object: {}): T; 
    static deserialize<T extends Model>(this: Function & { prototype: Model }, ctor: (new() => T), object: {}); 
    static deserialize<T extends Model>(this: (new() => T), first: (new() => T) | {}, second?: any) { 
     return typeof first === "function" ? new first() : new this(); 
    } 

    static deserializeArray<T extends Model>(this: (new() => T), ...objects: {}[]): T[]; 
    static deserializeArray<T extends Model>(this: Function & { prototype: Model }, ctor: (new() => T), ...objects: {}[]): T[]; 
    static deserializeArray<T extends Model>(this: (new() => T), first: (new() => T) | {}[], second?: {}[]): T[] { 
     const ctor = typeof first === "function" ? first : this; 
     const objects = typeof first === "function" ? second : first; 
     return objects.map(object => Model.deserialize(ctor, object)); 
    } 
} 

這允許兩種形式同時保留abstract

+0

第二種形式有什麼意義?如果你可以做'MyModel.deserialize({});'爲什麼你需要做'Model.deserialize(MyModel,{});'? –

+0

是的,但是如果你有'ModelType',那麼你可以簡單地做'ModelType。反序列化(...)'。它肯定會擴展'Model',因此它有一個對靜態'deserialize'函數的引用。 –

+0

我無法調用'ModelType.deserialize()'。如果我重構包含'ModelCtor '類型(其具有反序列化簽名),則'MyModel'不能分配給'ModelCtor '。 –

回答

0

在我看來,有沒有必要有兩個:

let myModel1 = MyModel.deserialize({}); 
let myModel2 = Model.deserialize(MyModel, {}); 

第一種形式是足夠的,更具可讀性,然後代碼將是這樣的:

type ModelCtor<T extends Model> = { 
    new(): T; 
    deserialize<T extends Model>(object: any): T; 
    deserializeArray<T extends Model>(...objects: any[]): T[]; 
}; 

abstract class Model { 
    static deserialize<T extends Model>(this: ModelCtor<T>, object: any): T { 
     return new this(); 
    } 

    static deserializeArray<T extends Model>(this: ModelCtor<T>, ...objects: any[]): T[] { 
     return objects.map(object => this.deserialize(object)); 
    } 
} 

class MyModel extends Model { } 

let myModel = MyModel.deserialize({}); 
let myModels = MyModel.deserializeArray({}, {}, {}); 

code in playground

但是,如果由於某種原因超出了我的範圍,您確實希望擁有這兩種形式,那麼您可以執行如下操作:

type ModelCtor<T extends Model> = { 
    new(): T; 
    deserialize<T extends Model>(object: any): T; 
    deserialize<T extends Model>(ctor: ModelCtor<T>, object: any): T; 

    deserializeArray<T extends Model>(...objects: any[]): T[]; 
    deserializeArray<T extends Model>(ctor: ModelCtor<T>, ...objects: any[]): T[]; 
}; 

class Model { 
    static deserialize<T extends Model>(this: ModelCtor<T>, object: any): T; 
    static deserialize<T extends Model, S extends Model>(this: ModelCtor<T>, ctor: ModelCtor<S>, object: any): S; 
    static deserialize<T extends Model, S extends Model>(this: ModelCtor<T>, first: ModelCtor<S> | any, second?: any): S { 
     return second === undefined ? new this() : new first(); 
    } 

    static deserializeArray<T extends Model>(this: ModelCtor<T>, ...objects: any[]): T[]; 
    static deserializeArray<T extends Model, S extends Model>(this: ModelCtor<T>, ctor: ModelCtor<S>, ...objects: any[]): S[]; 
    static deserializeArray<T extends Model, S extends Model>(this: ModelCtor<T>, ...objects: any[]): S[] { 
     const ctor: ModelCtor<S> = typeof objects[0] === "function" ? objects[0] : this as any; 
     return objects.map(object => ctor.deserialize(ctor, object)); 
    } 
} 

class MyModel extends Model { } 

let myModel1 = MyModel.deserialize({}); 
let myModels1 = MyModel.deserializeArray({}, {}, {}); 

let myModel2 = Model.deserialize(MyModel, {}); 
let myModels2 = Model.deserializeArray(MyModel, {}, {}, {}); 

code in playground

的代碼更compilated(我認爲沒有充分的理由)。
另外,我不得不從Model刪除abstract部分,否則有此錯誤:

The 'this' context of type 'typeof Model' is not assignable to method's 'this' of type 'ModelCtor'.
Cannot assign an abstract constructor type to a non-abstract constructor type.

對於這兩個行:

let myModel2 = Model.deserialize(MyModel, {}); 
let myModels2 = Model.deserializeArray(MyModel, {}, {}, {}); 

也許你可以用它玩多一點和修復那些錯誤沒有刪除abstract

+0

謝謝,這絕對是指向正確的方向。我已經更新了這個問題,以包含一個保留'abstract'的解決方案。 –