有没有办法在打字稿中用mixins来模拟super?

问题描述 投票:0回答:2

我通过 Mixins 使用多重继承(使用替代模式)。有没有办法用这种模式获得类似于“超级”的东西?考虑这里的例子

abstract class Activatable{
    private activated: boolean = false;
    constructor(){}
    public activate(): void {
        this.activated = true;
        console.log('Activatable activated')
  }
}

class SomethingElse{};

interface ThingParent extends Activatable{}

class ThingParent extends SomethingElse{
    public activate(): void {
        (this as Activatable).activate();
        let addSomeValueHere = true;
        console.log('Thing parent activated')
    }
}

applyMixins(ThingParent, [Activatable]);

class Thing extends ThingParent {
    constructor(){
        super()
    }
    public activate(): void {
        super.activate();
        
        console.log('Thing activated');
    }
}

let thing = new Thing();
thing.activate();



function applyMixins(derivedCtor: any, constructors: any[]) {
  constructors.forEach((baseCtor) => {
    Object.getOwnPropertyNames(baseCtor.prototype).forEach((name) => {
      Object.defineProperty(
        derivedCtor.prototype,
        name,
        Object.getOwnPropertyDescriptor(baseCtor.prototype, name) ||
          Object.create(null)
      );
    });
  });
}

Thing 扩展了 ThingParent,后者通过 mixin 扩展了一个名为 Activatable 的类。当我在 Thing 上调用 activate 时,我也想在 ThingParent 和 Activatable 上调用 activate,但它只在 Activatable 上调用。

如果我更改 ThingParent 上的函数名称,我可以直接调用该函数,但 ThingParent 无法通过 super 访问 Activatable (因为它扩展了 SomethingElse)。然后我必须从 thing.activate() 调用 super.activate() 和 this.thingParentActivate() 所以这是我希望尽可能避免的模式。

还有其他选择吗?

(打字稿操场上的代码链接:https://www.typescriptlang.org/play?#code/IYIwzgLgTsDGEAJYBthjAggvAlgN2AlGQFMBvAWACgFaEAHKfQkhOCZiEgEwC4EQAe0GlgAOwQBeBADNgyMCQDc1OkkFjIUAK7xBUABQBKMgF9VdetpDIcsN rgJdj-PIJzcElGmroQAFjhgAHTsnDxSCNDayha+6poiJMHIggDmBgDk2BxOxKxhTjyZRhbmVOXUKGgYAMqCALYkaThiaQCiCuSmKlTurVxQcrCsACqBbQAKwFAkYogkAB5cYtwYOZz5ZtRVqOgI461p07PzCE srawj1TS1tnYrealY2dg65LC4Ibh5ecb4GFoYNBYRyEfJGUJg5xGXrxWikRDAbjcG4kABq8hiAAkSLNItFYj54rANGAkil0llDm0GDM5kjocVSsSEOVKlRqvsaWlzss5lceScGX9WTNNE9IYTP81GBtPQ8cZ-uVnt ZbPZCp8jK53J4nvD5YqoFCPjC4fDZXRxeTSJSMpkee9wtwShbaOydlRgPR6MgAJ4AWRwi1aYAMQvp8wanANAgANobPI2EgAXVhXuoiKiE150jEJAA7gcc8Zenc0qbwqWM30qDJtGJcBo2L6A8HQ5oDNw8 fgeABhCD6fjif2xm2SwdQMDDsT+unp0W0cc6KUhGT6dpwfwGAwgNAkAf6IxSAB8i7oAHkQAArEjwYJpZoXgtiSZQQTGiD+gBywCa4b3RRDxNRhBEHL9FUhdcoE3WBtwMMQ-xIY9JDPA01CvW9727GRWhIN8P zxL8DCtWhuyYPB+0nYJQPA-1FWjUiEEQppGNZDCbzvCAHyfF8CM-f0ABESDAWAmHoSdd33YCaPfOiGOYpDjwAH2UpjaEwrjgjEkhPjEbRkGQFl4nTVlTFMj1TNMIA)

typescript multiple-inheritance mixins
2个回答
1
投票

如您所知,JavaScript 类不支持多重继承,因此如果您想获得这种效果,您需要做一些事情来模拟它。 Mixins 是实现此目的的一种方法,但它们并不真正支持方法名称冲突。如果你有冲突的方法名称,那么 mixins 最终会破坏除其中一个之外的所有方法,这不是你想要的。

如果您想在“子类”的实例上调用特定“超类”的方法,您可以直接使用

Function.prototype.call()
并将实例作为
this
参数来执行此操作。您不调用
Activatable::activate()
,而是调用
Activatable.prototype.activate.call(this);

让我们尝试一下:

interface ThingParent extends Activatable { }
class ThingParent extends SomethingElse {
    public activate(): void {
        Activatable.prototype.activate.call(this);
        let addSomeValueHere = true;
        console.log('Thing parent activated')
    }
}

class Thing extends ThingParent {
    constructor() {
        super()
    }
    public activate(): void {
        ThingParent.prototype.activate.call(this);
        console.log('Thing activated');
    }
}

let thing = new Thing();
thing.activate();
// [LOG]: "Activatable activated" 
// [LOG]: "Thing parent activated" 
// [LOG]: "Thing activated" 

编译正常并产生您期望的输出。


在更复杂的场景中,您可能决定需要组合使用 mixin 和直接方法

call
。或者其他一些方法。

Playground 代码链接


0
投票

谢谢@jcalz!

有 Angular 示例如何从 mixin 调用 super.ngOnInit。

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

export function WithTheMixin<BaseType extends Constructor<object>>(Base: BaseType = (class {} as any)):any {
    return class extends Base implements OnInit{
      
        ngOnInit(): void {
            // super on Init
            if (Base.prototype && Base.prototype.ngOnInit){
                Base.prototype.ngOnInit.call(this);
            }
            console.log('Mixin onInit');
        }

    }
}


@Component({
    template: '',
    styles: []
})
export class TheAbstractComponent implements OnInit {

    ngOnInit(): void {
        console.log('TheAbstractComponent onInit');
    }
}

@Component({
    selector: 'the-component',
    template: '<span>hello!</span>'
})
export class TheComponent extends WithNavMenuData(TheAbstractComponent)  {
    
}
© www.soinside.com 2019 - 2024. All rights reserved.