【问题标题】:Typescript: polymorhpic this, overload signature is not compatible with function implementation打字稿:多态this,重载签名与函数实现不兼容
【发布时间】:2017-09-16 02:08:55
【问题描述】:

谁能解释为什么这不起作用:

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();
    }
}

然后是通用服务(角度)来请求模型:

@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。

【问题讨论】:

  • 第二种形式的意义何在?如果你可以做MyModel.deserialize({}); 为什么你需要做Model.deserialize(MyModel, {});?
  • 是的,但如果你有ModelType,那么你可以简单地使用ModelType. deserialize(...)。它肯定扩展了Model,因此它引用了静态deserialize 函数。
  • 我无法致电ModelType.deserialize()。如果我重构以包含您的 ModelCtor&lt;T&gt; 类型(具有反序列化签名),则 MyModel 不可分配给 ModelCtor&lt;T&gt;。

标签: typescript


【解决方案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({}, {}, {});

(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)

代码更加编译(我认为没有充分的理由)。
另外,我必须从Model 中删除abstract 部分,否则会出现此错误:

“typeof Model”类型的“this”上下文不可分配给“ModelCtor”类型的方法“this”。
无法将抽象构造函数类型分配给非抽象构造函数类型。

对于这两行:

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

也许您可以多尝试一下,并在不删除abstract 的情况下修复这些错误。

【讨论】:

  • 谢谢,这无疑为我指明了正确的方向。我已更新问题以包含保留 abstract 的解决方案。
猜你喜欢
  • 2017-01-17
  • 1970-01-01
  • 2019-01-24
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2012-11-13
  • 1970-01-01
相关资源
最近更新 更多