Typescript:多态的 this,重载签名与函数实现不兼容
有人能解释一下为什么这不起作用吗:
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 报告“重载签名与函数实现不兼容”。
为什么两种形式都需要?
考虑一个返回序列化(json)模型的 REST API:
class MyModelController {
get(id: number) {
let myModel = ... some db/service call ...
return myModel.serialize();
}
}
然后是一个通用的服务(Angular)用于请求模型:
@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。
答案#1
在我看来,没有必要两者都要:
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({}, {}, {});
(在游乐场中的代码)
然而,如果出于某种我无法理解的原因,你确实想同时拥有这两种形式,那么你可以这样做:
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, {}, {}, {});
(在游乐场中的代码)
代码更复杂了(我认为没有充分的理由)。
另外,我不得不从 Model 中移除 abstract 部分,否则会出现这个错误:
类型为 'typeof Model' 的 'this' 上下文不能分配给类型为 'ModelCtor' 的方法的 'this'。
不能将抽象构造函数类型分配给非抽象构造函数类型。
对于这两行:
let myModel2 = Model.deserialize(MyModel, {});
let myModels2 = Model.deserializeArray(MyModel, {}, {}, {});
也许你可以再多试一试,并修复那些错误,而不删除abstract。
