TypeScript中的接口和泛化

时间:2019-05-27 21:50:58

标签: angular typescript

我在TypeScript中有四个实体(模型),如下所示:

基类/模型:

export interface BaseClass {
  id: string;
  entityId: string;
  entityName: string;
}

儿童班1:

import { BaseClass } from './base-class';

export interface ChildClass1 extends BaseClass {
  commentId: string;
  authorName: string;
}

儿童班2:

import { BaseClass } from './base-class';

export interface ChildClass2 extends BaseClass {
  authorName: string;
}

儿童3级:

import { BaseClass } from './base-class';

export interface ChildClass3 extends BaseClass {
  operationType: string;
}

在我的组件中,如何像Java中一样实现概括。

例如:有一个名为的对象

public models: BaseClass[] = [];

如何在上面的模型对象中显示不同类型的对象,例如ChildClass1ChildClass2ChildClass3

编辑:

以下是显示Visual Studio Code中错误的屏幕截图。

enter image description here

1 个答案:

答案 0 :(得分:4)

是的,您可以这样做。您可能遇到的问题是,当您使用“新鲜”对象文字(即尚未分配给某个变量的对象)并将其分配给指定类型的变量时,TypeScript使用{{3} }警告您是否要添加意外的属性。 TypeScript中的类型通常被视为开放/可扩展/可泛型,但是在这种情况下,它们被视为封闭/ excess property checks,因为这样做通常是 错误。

const excessPropertyErrors: BaseClass[] = [
  { id: "a", entityId: "b", entityName: "c", commentId: "d", authorName: "e" },
  { id: "f", entityId: "g", entityName: "h", authorName: "i" },
  { id: "j", entityId: "k", entityName: "l", operationType: "m" }
]; // error! excess property checks are biting us

那么,如何防止这种情况发生在我们身上?实际上,各种各样的方式。


其中之一是明确提及我们期望的子类。类型BaseClassBaseClass | ChildClass1 | ChildClass2 | ChildClass3在结构上是相同的,但是它们在看待“额外”属性方面有所不同:

const explicitlyMentionSubclasses: 
 Array<BaseClass | ChildClass1 | ChildClass2 | ChildClass3> = [
  { id: "a", entityId: "b", entityName: "c", commentId: "d", authorName: "e" },
  { id: "f", entityId: "g", entityName: "h", authorName: "i" },
  { id: "j", entityId: "k", entityName: "l", operationType: "m" }
]; // okay

如果您拼错operationType,那仍然会引发多余的属性错误,因此您可能需要以某种方式提及类型。尽管如此,让变量的类型携带子类仍然是一种限制。


执行此操作的另一种方法是将新鲜的对象文字分配给使用您关心的特定子类类型注释的变量,然后将这些变量放入数组中:

const childClass1: ChildClass1 = 
  { id: "a", entityId: "b", entityName: "c", commentId: "d", authorName: "e" };
const childClass2: ChildClass2 = 
  { id: "f", entityId: "g", entityName: "h", authorName: "i" };
const childClass3: ChildClass3 = 
  { id: "j", entityId: "k", entityName: "l", operationType: "m" };
const individualElements: BaseClass[] = [childClass1, childClass2, childClass3]; // okay

这是您执行此操作最安全的方法之一,因为每个变量childClass1childClass2childClass3都会受到约束,并且会检查相关子类的超额属性。

>

您可以执行此操作的另一种方法是,通过使对象文字不再“新鲜”来完全关闭多余的属性检查。首先,将数组分配给未注释的变量;该类型将由编译器推断。然后,将推断类型的变量分配给BaseClass[]变量:

const completelyInferred = [
  { id: "a", entityId: "b", entityName: "c", commentId: "d", authorName: "e" },
  { id: "f", entityId: "g", entityName: "h", authorName: "i" },
  { id: "j", entityId: "k", entityName: "l", operationType: "m" }
];
const noLongerFresh: BaseClass[] = twoStepProcess1; // okay

这可行,但是请记住,除了BaseClass成员之外,绝对不会检查任何东西。如果您将operationType设为数字,则该数字仍会被接受。


您可以决定希望BaseClass永远不要进行过多的属性检查。一种完全关闭该方法的方法是定义一个BaseClass扩展名,该扩展名明确允许任何类型的任何多余属性:

interface BaseClassWithUnknownExtraProperties extends BaseClass {
  [k: string]: unknown;  // add index signature
}
const indexSignature: BaseClassWithUnknownExtraProperties[] = [
  { id: "a", entityId: "b", entityName: "c", commentId: "d", authorName: "e" },
  { id: "f", entityId: "g", entityName: "h", authorName: "i" },
  { id: "j", entityId: "k", entityName: "l", operationType: "m" }
]; // okay

最后,您可能想使用一个辅助函数来接受BaseClass的扩展名(这将关闭多余的属性检查):

const asBaseClassArray = <T extends BaseClass>(arr: T[]) => arr;
const usingHelperFunction: BaseClass[] = asBaseClassArray([
  { id: "a", entityId: "b", entityName: "c", commentId: "d", authorName: "e" },
  { id: "f", entityId: "g", entityName: "h", authorName: "i" },
  { id: "j", entityId: "k", entityName: "l", operationType: "m" }
]); // okay

希望其中一种方法可以为您提供一些指导。祝你好运!

exact