最新消息:雨落星辰是一个专注网站SEO优化、网站SEO诊断、搜索引擎研究、网络营销推广、网站策划运营及站长类的自媒体原创博客

Angular typing: Why won't Typescript treat a component subclass as assignable to its superclass? - Stack Overflow

programmeradmin3浏览0评论

I have two Angular components which share a lot of common appearance and behavior, so much of that commonality is in an abstract superclass. Here's the start of that superclass:

@Directive({ standalone: true })
export abstract class DigitSequenceEditorDirective<T> implements
    AfterViewInit, ControlValueAccessor, OnInit, OnDestroy, Validator {
  ...

And this is how the two subclasses are declared:

@Component({
  selector: 'tbw-angle-editor',
  animations: [BACKGROUND_ANIMATIONS],
  templateUrl: '../digit-sequence-editor/digit-sequence-editor.directive.html',
  styleUrls: ['../digit-sequence-editor/digit-sequence-editor.directive.scss'],
  providers: [{ provide: NG_VALUE_ACCESSOR, useExisting: forwardRef(() => AngleEditorComponent), multi: true },
              { provide: NG_VALIDATORS, useExisting: forwardRef(() => AngleEditorComponent), multi: true }],
  imports: [NgStyle, NgClass],
  standalone: true
})
export class AngleEditorComponent extends DigitSequenceEditorDirective<number> implements OnInit {
  ...
@Component({
  selector: 'tbw-time-editor',
  animations: [BACKGROUND_ANIMATIONS],
  templateUrl: '../digit-sequence-editor/digit-sequence-editor.directive.html',
  styleUrls: ['../digit-sequence-editor/digit-sequence-editor.directive.scss', './time-editorponent.scss'],
  providers: [{ provide: NG_VALUE_ACCESSOR, useExisting: forwardRef(() => TimeEditorComponent), multi: true },
              { provide: NG_VALIDATORS, useExisting: forwardRef(() => TimeEditorComponent), multi: true }],
  imports: [NgClass, NgStyle],
  standalone: true
})
export class TimeEditorComponent extends DigitSequenceEditorDirective<number> implements OnInit {
  ...

(A lot of redundant boilerplate in the declarations, but no way to reduce that much that I know of.)

I would think (but apparently I am wrong) I should be able to pass either AngleEditorComponent or TimeEditorComponent to a function with a generic type that extends DigitSequenceEditorDirective<any>, something I need to do to effectively consolidate a lot of common Karma/Jasmine test code. Part of that common code looks like this:

type DigitSequenceSuperclass = DigitSequenceEditorDirective<any>;

export class CommonTestEnvironment<T, U extends DigitSequenceSuperclass> {

  // ...


export async function sharedBeforeEach<T, U extends DigitSequenceSuperclass>(qlass: Type<T>, innerClass: U,
                                          selector: string, initValue: string): Promise<CommonTestEnvironment<T, U>> {
  await TestBed.configureTestingModule({
  // ...

Unless I just hack around the issue with @ts-ignore, this is the grief I get:

So, DigitSequenceEditorDirective<T> is not a class derived from... itself?

Can anyone explain what might be happening here, and offer a better work around than @ts-ignore?

The unit testing is working fine, by the way, using the common code. I'd just prefer cleaner typing if I can manage it.

Full source code here:

Update:

instanceof DigitSequenceEditorDirective evaluates as true for instances of both AngleEditorComponent and TimeEditorComponent.

I have two Angular components which share a lot of common appearance and behavior, so much of that commonality is in an abstract superclass. Here's the start of that superclass:

@Directive({ standalone: true })
export abstract class DigitSequenceEditorDirective<T> implements
    AfterViewInit, ControlValueAccessor, OnInit, OnDestroy, Validator {
  ...

And this is how the two subclasses are declared:

@Component({
  selector: 'tbw-angle-editor',
  animations: [BACKGROUND_ANIMATIONS],
  templateUrl: '../digit-sequence-editor/digit-sequence-editor.directive.html',
  styleUrls: ['../digit-sequence-editor/digit-sequence-editor.directive.scss'],
  providers: [{ provide: NG_VALUE_ACCESSOR, useExisting: forwardRef(() => AngleEditorComponent), multi: true },
              { provide: NG_VALIDATORS, useExisting: forwardRef(() => AngleEditorComponent), multi: true }],
  imports: [NgStyle, NgClass],
  standalone: true
})
export class AngleEditorComponent extends DigitSequenceEditorDirective<number> implements OnInit {
  ...
@Component({
  selector: 'tbw-time-editor',
  animations: [BACKGROUND_ANIMATIONS],
  templateUrl: '../digit-sequence-editor/digit-sequence-editor.directive.html',
  styleUrls: ['../digit-sequence-editor/digit-sequence-editor.directive.scss', './time-editorponent.scss'],
  providers: [{ provide: NG_VALUE_ACCESSOR, useExisting: forwardRef(() => TimeEditorComponent), multi: true },
              { provide: NG_VALIDATORS, useExisting: forwardRef(() => TimeEditorComponent), multi: true }],
  imports: [NgClass, NgStyle],
  standalone: true
})
export class TimeEditorComponent extends DigitSequenceEditorDirective<number> implements OnInit {
  ...

(A lot of redundant boilerplate in the declarations, but no way to reduce that much that I know of.)

I would think (but apparently I am wrong) I should be able to pass either AngleEditorComponent or TimeEditorComponent to a function with a generic type that extends DigitSequenceEditorDirective<any>, something I need to do to effectively consolidate a lot of common Karma/Jasmine test code. Part of that common code looks like this:

type DigitSequenceSuperclass = DigitSequenceEditorDirective<any>;

export class CommonTestEnvironment<T, U extends DigitSequenceSuperclass> {

  // ...


export async function sharedBeforeEach<T, U extends DigitSequenceSuperclass>(qlass: Type<T>, innerClass: U,
                                          selector: string, initValue: string): Promise<CommonTestEnvironment<T, U>> {
  await TestBed.configureTestingModule({
  // ...

Unless I just hack around the issue with @ts-ignore, this is the grief I get:

So, DigitSequenceEditorDirective<T> is not a class derived from... itself?

Can anyone explain what might be happening here, and offer a better work around than @ts-ignore?

The unit testing is working fine, by the way, using the common code. I'd just prefer cleaner typing if I can manage it.

Full source code here: https://github/kshetline/tubular_ng-widgets/tree/development

Update:

instanceof DigitSequenceEditorDirective evaluates as true for instances of both AngleEditorComponent and TimeEditorComponent.

Share Improve this question edited Mar 18 at 9:05 kshetline asked Mar 18 at 3:56 kshetlinekshetline 13.8k6 gold badges49 silver badges91 bronze badges
Add a comment  | 

1 Answer 1

Reset to default 2

Ah hah! I got screwed by an auto-import.

This import was automatically created for me:

import { DigitSequenceEditorDirective } from 'tubular-ng-widgets';

...when what I really needed was:

import { DigitSequenceEditorDirective } from './digit-sequence-editor.directive';

For my type definitions to be consistent they needed to point to the same source. The errors seemed nonsensical because there were doubled-up definitions of the same classes.

与本文相关的文章

发布评论

评论列表(0)

  1. 暂无评论