[angular] How to extend / inherit components?

Now that TypeScript 2.2 supports Mixins through Class expressions we have a much better way to express Mixins on Components. Mind you that you can also use Component inheritance since angular 2.3 (discussion) or a custom decorator as discussed in other answers here. However, I think Mixins have some properties that make them preferable for reusing behavior across components:

  • Mixins compose more flexibly, i.e. you can mix and match Mixins on existing components or combine Mixins to form new Components
  • Mixin composition remains easy to understand thanks to its obvious linearization to a class inheritance hierarchy
  • You can more easily avoid issues with decorators and annotations that plague component inheritance (discussion)

I strongly suggest you read the TypeScript 2.2 announcement above to understand how Mixins work. The linked discussions in angular GitHub issues provide additional detail.

You'll need these types:

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

export class MixinRoot {
}

And then you can declare a Mixin like this Destroyable mixin that helps components keep track of subscriptions that need to be disposed in ngOnDestroy:

export function Destroyable<T extends Constructor<{}>>(Base: T) {
  return class Mixin extends Base implements OnDestroy {
    private readonly subscriptions: Subscription[] = [];

    protected registerSubscription(sub: Subscription) {
      this.subscriptions.push(sub);
    }

    public ngOnDestroy() {
      this.subscriptions.forEach(x => x.unsubscribe());
      this.subscriptions.length = 0; // release memory
    }
  };
}

To mixin Destroyable into a Component, you declare your component like this:

export class DashboardComponent extends Destroyable(MixinRoot) 
    implements OnInit, OnDestroy { ... }

Note that MixinRoot is only necessary when you want to extend a Mixin composition. You can easily extend multiple mixins e.g. A extends B(C(D)). This is the obvious linearization of mixins I was talking about above, e.g. you're effectively composing an inheritnace hierarchy A -> B -> C -> D.

In other cases, e.g. when you want to compose Mixins on an existing class, you can apply the Mixin like so:

const MyClassWithMixin = MyMixin(MyClass);

However, I found the first way works best for Components and Directives, as these also need to be decorated with @Component or @Directive anyway.

Examples related to angular

error NG6002: Appears in the NgModule.imports of AppModule, but could not be resolved to an NgModule class error TS1086: An accessor cannot be declared in an ambient context in Angular 9 TS1086: An accessor cannot be declared in ambient context @angular/material/index.d.ts' is not a module Why powershell does not run Angular commands? error: This is probably not a problem with npm. There is likely additional logging output above Angular @ViewChild() error: Expected 2 arguments, but got 1 Schema validation failed with the following errors: Data path ".builders['app-shell']" should have required property 'class' Access blocked by CORS policy: Response to preflight request doesn't pass access control check origin 'http://localhost:4200' has been blocked by CORS policy in Angular7

Examples related to typescript

TS1086: An accessor cannot be declared in ambient context Element implicitly has an 'any' type because expression of type 'string' can't be used to index Angular @ViewChild() error: Expected 2 arguments, but got 1 Typescript: No index signature with a parameter of type 'string' was found on type '{ "A": string; } Understanding esModuleInterop in tsconfig file How can I solve the error 'TS2532: Object is possibly 'undefined'? Typescript: Type 'string | undefined' is not assignable to type 'string' Typescript: Type X is missing the following properties from type Y length, pop, push, concat, and 26 more. [2740] Can't perform a React state update on an unmounted component TypeScript and React - children type?

Examples related to inheritance

How to extend / inherit components? Inheritance with base class constructor with parameters Class is not abstract and does not override abstract method Why not inherit from List<T>? Can an interface extend multiple interfaces in Java? How to call Base Class's __init__ method from the child class? How should I have explained the difference between an Interface and an Abstract class? JavaScript OOP in NodeJS: how? When do I have to use interfaces instead of abstract classes? C++ calling base class constructors

Examples related to angular-directive

Get Value From Select Option in Angular 4 How to get element's width/height within directives and component? is there any alternative for ng-disabled in angular2? How to extend / inherit components?