在打字稿混合中访问基类类型的问题

时间:2017-08-31 01:45:26

标签: typescript mixins

我有一些ES6 mixin代码,如下所示:

function DeserializableMixin(Base) {
  return class extends Base {
    deserialize(structure) {
      Object.assign(this, JSON.parse(JSON.stringify(structure)));
      return this;
    }
    clone() {
      const c = this.constructor;
      return new c().deserialize(this);
    }
  };
}

这适用于现有代码:

class FooEntity {}

class DeFoo extends DeserializableMixin(FooEntity) {}

const df = new DeFoo();
const x = df.deserialize({ bar: 43 });
const y = df.clone();

我想将这个mixin移植到TS并基于https://www.typescriptlang.org/docs/handbook/release-notes/typescript-2-2.html#support-for-mix-in-classes的文档,它似乎相当简单。

我希望反序列化为某种类型安全并采用Partial<Base>而不是Partial<any>Partial<{}>,以便IDE /编译器可以拾取与序列形状不匹配的反序列化基类。

同样我不想使用Partial<this>,因为这会导致派生类上的函数(例如clone()和deserialize())被允许作为结构的有效部分。

我想写的是:

export type Constructor<T> = new(...args : any[]) => T;

export function DeserializableMixin<T extends Constructor<{}>>(Base : T) {
  return class extends Base {
    deserialize(structure : Partial<Base> ) {
      Object.assign(this, JSON.parse(JSON.stringify(structure)));

      return this;
    }

    clone() {
      const c = this.constructor as Constructor<this>;

      return new c().deserialize(this as Base);
    }
  };
}

但是typescript不允许我使用Base作为类类型,即使它允许我在extends子句中使用它。

我接近它的唯一方法是执行以下操作:

export function DeserializableMixin<P, T extends Constructor<{}>>(Base : T, proto : P) {
  return class extends Base {
    deserialize(structure : Partial<P> ) {
      Object.assign(this, JSON.parse(JSON.stringify(structure)));

      return this;
    }

    clone() {
      const c = this.constructor as Constructor<this>;

      return new c().deserialize(this as any as P);
    }
  };
}

这要求我像这样使用mixin:

class Foo {
  bar : number;
  baz : string;
};

class DeFoo extends DeserializableMixin(Foo, Foo.prototype) {}

const df = new DeFoo();
const x = df.deserialize({bar: 43})
const y = df.clone();

做了这项工作,但对我来说似乎有点愚蠢。是否有一种不那么复杂的方式呢?

修改

我后来发现即使这还不够。如果混合的使用保留在与定义混合的模块相同的模块中,那么这很好,但如果我为此导出类型定义并在另一个模块中使用它,例如:

const df = new DeFoo();
const df2 = df.clone();

无法按预期工作,因为df2将具有any类型。请注意,如果它是在同一个模块中完成的(定义了混合模块),df2的类型为DeFoo。

为了解决这个问题,我添加了更多看似冗余的代码。

export function DeserializableMixin<P, T extends Constructor<{}>>(Base : T, proto : P) {
  return class extends Base {
    deserialize<E>(this : E, structure : Partial<P> ) : E {
      Object.assign(this, JSON.parse(JSON.stringify(structure)));

      return this;
    }

    clone<E>(this : E) : E {
      const c = this.constructor as any;

      return new c().deserialize(this as any as P);
    }
  };
}

0 个答案:

没有答案