【问题标题】:Typescript mixin: the return type of mixed methodTypescript mixin:混合方法的返回类型
【发布时间】:2018-10-16 10:27:02
【问题描述】:

我按照那边Typescript Mixins的例子:mixin,做了一些改动,

以下代码显示了关系:Point

    class Point {
  constructor(public x: number, public y: number) { }
  export() {
    return { x: this.x, y: this.y };
  }
}
type Constructor<T> = new (...args: any[]) => T;
function Tagged<T extends Constructor<Point>>(Base: T) {
  return class extends Base {
    _tag: string;
    constructor(...args: any[]) {
      super(...args);
      this._tag = "";
    }
    export() {
      return {
        ...super.export(),
        _tag: this._tag,
      };
    }
  };
}
function Madded<T extends Constructor<Point>>(Base: T) {
  return class extends Base {
    _mad: string;
    constructor(...args: any[]) {
      super(...args);
      this._mad = "";
    }
    export() {
      return {
        ...super.export(),
        _mad: this._mad,
      };
    }
  };
}

const TaggedPoint = Tagged(Point);

let tag = new TaggedPoint(10, 20);
tag._tag = "hello";
// typescript does not complaint about ._tag
console.log(tag.export()._tag);


const MadedTaggedPoint = Madded(TaggedPoint);
const mad = new MadedTaggedPoint(10, 20);
// typescript complaints:
// [ts] Property '_tag' does not exist on type '{ _mad: string; x: number; y: number; }'.
// because MadedTaggedPoint mixed TaggedPoint, so I thought mad.export() shuold have _tag property
console.log(mad.export()._tag);

【问题讨论】:

  • 你有几个错别字..console.log(point.export()._mad); 应该是console.log(mad.export()._mad); 而在Madded 你导出_tag 而不是_mad。但这里也有一个真正的问题。如果您做出我建议的更改,最终的mad.export() 将返回一个带有_mad 但不是_tag 的对象这是您真正的问题吗?
  • 当然,我纠正了错别字,因为 MadedTaggedPoint 混合了 TaggedPoint,所以我认为 mad.export() 应该有 _tag 属性

标签: typescript mixins


【解决方案1】:

问题在于 mixins 创建结果类型的方式。如果我们查看导出方法的最终类型,我们会看到它有 3 个重载:

export(): { _mad: string; x: number; y: number;}
export(): { _tag: string; x: number; y: number;}
export(): { x: number; y: number;}

所以问题在于,您最终会为它创建多个重载,而不是覆盖该方法。我怀疑mixin的实例类型最终是TNewMethods &amp; InstanceType&lt;T&gt;

如果我们对类型进行一些手术以替换 export 方法,我们可以覆盖实例类型:

class Point {
    constructor(public x: number, public y: number) { }
    export() {
        return { x: this.x, y: this.y };
    }
}
type Constructor<T> = new (...args: any[]) => T;
type OverrideExportReturn<T extends Constructor<Point>, TNewReturn> = {
    new (...args: (T extends new (...a: infer A) => any ? A: [])):
        { export(): ReturnType<InstanceType<T>['export']> & TNewReturn } & 
        Pick<InstanceType<T>, Exclude<keyof InstanceType<T>, 'export'>>
} & Pick<T, keyof T>

function Tagged<T extends Constructor<Point>>(Base: T){
    class Tagged extends Base {
        _tag: string;
        constructor(...args: any[]) {
            super(...args);
            this._tag = "";
        }
        export() {
            return {
                ...super.export(),
                _tag: this._tag,
            };
        }
    };
    return Tagged as unknown as OverrideExportReturn<typeof Tagged, {_tag: string }>;
}
function Maded<T extends Constructor<Point>>(Base: T) {
    class Maded extends Base {
        _mad: string;
        constructor(...args: any[]) {
            super(...args);
            this._mad = "";
        }
        export() {
            return {
                ...super.export(),
                _mad: this._mad,
            };
        }
    };
    return Maded as unknown as OverrideExportReturn<typeof Maded, {_mad: string }>;
}

const TaggedPoint = Tagged(Point);

let point = new TaggedPoint(10, 20);
point._tag = "hello";
console.log(point.export()._tag);


const MadedTaggedPoint = Maded(TaggedPoint);
const mad = new MadedTaggedPoint(10, 20);
console.log(mad.export()._mad);
console.log(mad.export()._tag);

【讨论】:

    猜你喜欢
    • 2022-10-12
    • 2013-05-18
    • 2021-04-05
    • 1970-01-01
    • 2022-11-04
    • 1970-01-01
    • 1970-01-01
    • 2017-01-28
    • 2020-09-07
    相关资源
    最近更新 更多