Ссылка на родительский класс typescript или его дочерний класс в шаблоне углового HTML

#html #angular #typescript #inheritance

Вопрос:

У меня есть проблема с наследованием машинописного текста и ссылкой на свойства в угловом HTML-шаблоне, и я долго не мог ее решить. (если это дублирование вопроса, извините за это, я не смог найти такой вопрос).

Итак, у меня есть родительский интерфейс, пусть он вызывает DummyParent с некоторыми свойствами.

 export interface DummyParent {
    dummy1?: string;
    dummy2?: number;
}
 

И у меня есть двое детей от этого интерфейса.

 export interface DummyChild1 extends DummyParent {
    foo1?: string;
    bar1: number;
}
export interface DummyChild2 extends DummyParent {
    foo2?: string;
    bar2: number;
}
 

И у меня есть угловой компонент, который получает входные данные, и его тип может быть копией всех этих интерфейсов.

 @Input() dummy!: DummyParent | DummyChild1 | DummyChild2;
 

Но когда у меня есть ссылка в HTML-шаблоне для свойства DummyChild, я получил ошибку.
Например:

 <ng-container *ngIf="dummy.foo1"></ng-container> 
Error: Property 'foo1' does not exist on type 'DummyParent'.
 

Основываясь на этой ошибке, кажется, что свойство dummy-это просто объект DummyParent, и я не могу ссылаться на него как на объект DummyChild1 или DummyChild2. Мой вопрос в том, как я должен создать экземпляр @Input() dummy, чтобы он был типом DummyParent ИЛИ DummyChild1 ИЛИ DummyChild2.
Я знаю, что могу решить проблему с использованием объекта типа»любой» или приведения объекта в HTML-шаблон, как это
<ng-контейнер *ngIf= «$any(фиктивный).foo1»>
но я стараюсь избегать использования этих решений.
Большое спасибо за любые ответы.

Ответ №1:

Вот как работают профсоюзы TS:

 export interface DummyParent {
    dummy1?: string;
    dummy2?: number;
}

export interface DummyChild1 extends DummyParent {
    foo1?: string;
    bar1: number;
}
export interface DummyChild2 extends DummyParent {
    foo2?: string;
    bar2: number;
}

type Union = DummyParent | DummyChild1 | DummyChild2

declare var union: Union;

// only dumm1 and dummy2 are available
union.dummy1
union.dummy2
 

union позволяет использовать общие свойства всех фиктивных типов. Поскольку каждый тип расширяется DummyParent , вам разрешается использовать только свойства из DummyParent .

Для того, чтобы сделать это безопасным, вы должны использовать пользовательские типы защиты:

 export interface DummyParent {
    dummy1?: string;
    dummy2?: number;
}

export interface DummyChild1 extends DummyParent {
    foo1?: string;
    bar1: number;
}
export interface DummyChild2 extends DummyParent {
    foo2?: string;
    bar2: number;
}

type Union = DummyParent | DummyChild1 | DummyChild2

declare var union: Union;

// only dumm1 and dummy2 are available
union.dummy1
union.dummy2


const isOne = (union: Union): union is DummyChild1 => 'bar1' in union
const isTwo = (union: Union): union is DummyChild2 => 'bar2' in union

if(isOne(union)){
    union.bar1 // ok
}

if(isTwo(union)){
    union.bar2 // ok
}
 

Тогда, я полагаю, вы можете сделать что-то подобное:

 <ng-container *ngIf="isTwo(union)">
 // do smth with union.bar2
</ng-container>
 

P.S. Я не на 100% уверен, что in оператор-лучшее решение для такого типа охранника.
Вы также можете рассмотреть это:

 const hasProperty = <Obj, Prop extends string>(obj: Obj, prop: Prop)
  : obj is Obj amp; Record<Prop, unknown> =>
  Object.prototype.hasOwnProperty.call(obj, prop);
 

Ответ №2:

Мой вопрос в том, как я должен создать экземпляр @Input() dummy, чтобы он был типом DummyParent ИЛИ DummyChild1 ИЛИ DummyChild2.

Именно этим вы сейчас и занимаетесь. Если вы говорите, что что-то относится к типу A, типу B или типу C, вы можете получить доступ только к свойствам, существующим для ВСЕХ ТРЕХ типов. В противном случае, какой смысл в явном вводе, если вы все еще можете получить доступ к несуществующим свойствам?

И поскольку в ваших случаях B и C наследуются после A, это будут только свойства типа A. Если вы хотите получить доступ к свойствам типа A, вы должны сузить тип до A (например, с помощью type guard).

Вы можете прочитать больше об этом в официальных документах TypeScript здесь.