5

Angular can Query subComponents by Types, which is used in Testing like this:

fixture.debugElement.query( By.directive( ComponentType ) );

Now i wanted to make a function which does this for me:

import { ComponentFixture } from '@angular/core/testing';
import { By } from '@angular/platform-browser';
import { Type } from '@angular/core';

export function findSubComponent<T>( fixture: ComponentFixture<any>, componentType: T & Type<any> ): T {
  const subComponentDebugElement = fixture.debugElement.query( By.directive( componentType ) );
  return subComponentDebugElement.componentInstance;
}

Now here comes the problem. My function currently returns typeof ComponentType instead of an actual object of ComponentType and therefore i can not access its properties.

The Type of subComponentDebugElement.componentInstance here is any, so i can just declare the type in the return Type argument (function (): T)

How can i turn T which stands for typeof ComponentInstance in this case into ComponentInstance?

Jeremias Nater
  • 650
  • 3
  • 18
  • 1
    Possibly `InstanceType` but since I can't easily use your example in a standalone IDE like [the TypeScript Playground (link here)](https://tsplay.dev/wX77JW) then I can't check. – jcalz Jan 27 '22 at 15:11
  • Thanks for providing this. InstanceType seems to be the correct way, but i can not use it since `InstanceType` `Type 'T' does not satisfy the constraint 'abstract new (...args: any) => any'.`. any clue on how i can define T further to fullfill those constraints? – Jeremias Nater Jan 27 '22 at 15:16
  • 1
    I have approximately zero clue until I have a [mre] I can just paste into a standalone IDE without unrelated errors, sorry. Maybe someone with angular-test specific knowledge can answer right away. Otherwise you might want to work on either removing third party dependencies from your example code or give the necessary import statements so that things work in the playground, or give a link to an external web IDE project with stuff already configured. Good luck! – jcalz Jan 27 '22 at 15:19
  • 1
    Okay maybe I have small but nonzero clue, you might just want `findSubComponent any>(...` but I still can't test it so ‍♂️ – jcalz Jan 27 '22 at 15:20
  • oh im sorry, as i understood the imports might help you to reproduce it? i added the imports and am now trying to test your solution – Jeremias Nater Jan 27 '22 at 15:24
  • you resolved my issue, it took me longer than it should have, but changing the first line to `export function findSubComponent any>( fixture: ComponentFixture, componentType: (T & Type) ): InstanceType {` fixed the issue. If you want to post an answer i will accecpt it, otherwise i can also post it myself later, but i will leave you the chance to do it, thanks alot, kind stranger! – Jeremias Nater Jan 27 '22 at 15:30
  • 1
    Feel free to post your own answer; I would do it, but I just ran into what looks like a nasty bug in the TypeScript playground, where one of your import statements causes the browser to go bonkers and eat up all CPU: https://tsplay.dev/w166KN (if you click that be ready to kill the tab) – jcalz Jan 27 '22 at 15:51

2 Answers2

8

InstanceType<T>

As suggested by @jcalz the solution to this was to use InstanceType<T> like this:

type AbstractClassType = abstract new ( ...args: any ) => any;

export function querySubComponent<T extends AbstractClassType>(...): InstanceType<T> {
...
}

use of the AbstractClassType as abstract new ( ...args: any ) => any

Please note that the AbstractClassType might not be needed with your existing type definition, but apparently the generic InstanceType<> needs to use a type with a constructor, otherwise i get the following TS-Error: Type 'T' does not satisfy the constraint 'abstract new (...args: any) => any'.

Jeremias Nater
  • 650
  • 3
  • 18
0

Basically the same answer, but more compact

function querySubComponent<T extends abstract new (...args: any) => any>(fixture: ComponentFixture<any>, componentType: T & Type<any>) {
    const subComponentDebugElement = fixture.debugElement.query( By.directive( componentType ) );
    return subComponentDebugElement.componentInstance as InstanceType<T>;
}
user3413723
  • 11,147
  • 6
  • 55
  • 64