title: Angular 5/6: Component Interface - is it possible?
body: <p>Here's what I'm trying to accomplish: create a component to wrap around MatStepper that will accept 2..n steps, each their own components.</p>
  
  <p>With that said, the way that I would know how to do that in other languages would be to create an interface with the common behavior, and implement it in the different components but within the wrapper component use the interface.</p>
  
  <p><strong>wizard-step.component.ts</strong></p>
  
  <pre><code>export interface WizardStep {
    isValid: boolean;
  
    nextClicked(e);
    previousClicked(e);
  }
  </code></pre>
  
  <p><strong>wizard.component.ts</strong></p>
  
  <pre><code>import { Component, Input, OnInit, ViewChild } from '@angular/core';
  import { ProgressBarType } from '../progress-bar/ProgressBarType';
  import { MatStepper } from '@angular/material';
  import { WizardStep } from './wizard-step.component';
  
  @Component({
    selector: 'app-wizard',
    templateUrl: './wizard.component.html',
    styleUrls: [ './wizard.component.css' ]
  })
  export class WizardComponent implements OnInit {
  
    progress: number;
    progressBarType = ProgressBarType.Progress;
  
    @Input() steps: WizardStep[] = [];
  
    /**
     * The material stepper instance.
     */
    @ViewChild('stepper') private stepper: MatStepper;
  
    constructor() {
    }
  
  
    ngOnInit() {
      this.calculateProgress(0);
    }
  
    public calculateProgress(index: number): void {
      this.progress = ((index + 1) / this.steps.length) * 100;
    }
  
    public next(e): void {
      this.steps[e.selectedIndex].nextClicked(e);
    }
  
    public previous(e): void {
      this.steps[e.selectedIndex].previousClicked(e);
    }
  
    public selectionChange(e):void {
      this.calculateProgress(e.selectedIndex);
    }
  }
  </code></pre>
  
  <p><strong>wizard.component.html</strong></p>
  
  <pre><code>&lt;panel&gt;
    &lt;progress-bar class="progress-margins" [progress]="progress" [type]="progressBarType"&gt;&lt;/progress-bar&gt;
    &lt;mat-horizontal-stepper class="hide-header" #stepper (selectionChange)="selectionChange($event)"&gt;
      &lt;mat-step *ngFor="let step of steps"&gt;
        &lt;ng-container *ngComponentOutlet="step"&gt;&lt;/ng-container&gt;
      &lt;/mat-step&gt;
    &lt;/mat-horizontal-stepper&gt;
  
    &lt;form-buttons
      primaryLabel="Next"
      (primaryButtonClicked)="stepper.next(); next($event)"
      secondaryLabel="Previous"
      (secondaryButtonClicked)="stepper.previous(); previous($event)"&gt;
    &lt;/form-buttons&gt;
  &lt;/panel&gt;
  </code></pre>
  
  <p>I'm then trying to use this component by creating a validation component with a component for each wizard step:</p>
  
  <p><strong>wizard-validation.component.ts</strong></p>
  
  <pre><code>import { Component, OnInit } from '@angular/core';
  import { WizardStep } from 'framework';
  import { WizardValidationStep1Component } from '../wizard-validation-step1/wizard-validation-step1.component';
  import { WizardValidationStep2Component } from '../wizard-validation-step2/wizard-validation-step2.component';
  import { WizardValidationStep3Component } from '../wizard-validation-step3/wizard-validation-step3.component';
  
  @Component({
    selector: 'app-wizard-validation',
    templateUrl: './wizard-validation.component.html',
    styleUrls: []
  })
  export class WizardValidationComponent implements OnInit {
  
    steps: WizardStep[] = [];
  
    constructor() {
    }
  
    ngOnInit() {
      this.steps.push(WizardValidationStep1Component);
      this.steps.push(WizardValidationStep2Component);
      this.steps.push(WizardValidationStep3Component);
    }
  
  }
  </code></pre>
  
  <p><strong>wizard-validation.component.html</strong></p>
  
  <pre><code>&lt;wizard [steps]="steps"&gt;
  &lt;/wizard&gt;
  </code></pre>
  
  <p>And finally, <strong>WizardValidationStep1Component</strong>, <strong>WizardValidationStep2Component</strong>, and <strong>WizardValidationStep3Component</strong> are identical at this point:</p>
  
  <pre><code>import { Component } from '@angular/core';
  
  import { WizardStep } from 'framework';
  
  @Component({
    selector: 'app-wizard-validation-step1',
    templateUrl: './wizard-validation-step1.component.html',
    styleUrls: []
  })
  export class WizardValidationStep1Component implements WizardStep {
  
    isValid: boolean;
    stepName = 'Step 1';
  
    constructor() {
      this.isValid = true;
    }
  
    nextClicked(e) {
      alert('Clicked next on ' + this.stepName);
    }
  
    previousClicked(e) {
      alert('Clicked previous on ' + this.stepName);
    }
  }
  </code></pre>
  
  <p>With <strong>wizard-validation-step1.component.html</strong>:</p>
  
  <pre><code>&lt;p&gt;
    wizard-validation-step1 works!
  &lt;/p&gt;
  </code></pre>
  
  <hr>
  
  <p>In <strong>wizard.component.ts</strong>, if I use an untyped array instead of a WizardStep array, it works beautifully but of course my step components don't register the clicks, etc.</p>
  
  <pre><code>@Input() steps = [];
  </code></pre>
  
  <p>When used as shown above, when I try to run <code>ng build</code>, I get the following messages:</p>
  
  <pre><code>ERROR in src/app/wizard-validation/wizard-validation.component.ts(20,21): error TS2345: Argument of type 'typeof WizardValidationStep1Component' is not assignable to parameter of type 'WizardStep'.
    Property 'isValid' is missing in type 'typeof WizardValidationStep1Component'.
  src/app/wizard-validation/wizard-validation.component.ts(21,21): error TS2345: Argument of type 'typeof WizardValidationStep2Component' is not assignable to parameter of type 'WizardStep'.
    Property 'isValid' is missing in type 'typeof WizardValidationStep2Component'.
  src/app/wizard-validation/wizard-validation.component.ts(22,21): error TS2345: Argument of type 'typeof WizardValidationStep3Component' is not assignable to parameter of type 'WizardStep'.
    Property 'isValid' is missing in type 'typeof WizardValidationStep3Component'.
  </code></pre>
  
  <p>Is the pattern I am trying to use valid in Angular 5/6? If not, is there another way to accomplish what I am trying to do?</p>