简体   繁体   English

TypeScript中的接口和泛化

[英]interfaces and generalisation in TypeScript

I have four entities (models) in TypeScript as follows: 我在TypeScript中有四个实体(模型),如下所示:

Base class/model: 基类/型号:

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

Child Class 1: 儿童班1:

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

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

Child Class 2: 儿童班2:

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

export interface ChildClass2 extends BaseClass {
  authorName: string;
}

Child Class 3: 儿童班3:

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

export interface ChildClass3 extends BaseClass {
  operationType: string;
}

In my component, how can I achieve generalisation just like we have in Java. 在我的组件中,如何像Java中一样实现概括。

For ex: Have an object named, 例如:有一个名为的对象,

public models: BaseClass[] = [];

How can I have different types of objects like ChildClass1 , ChildClass2 , ChildClass3 present in the models objects above. 我如何在上面的模型对象中拥有不同类型的对象,例如ChildClass1ChildClass2ChildClass3

EDIT: 编辑:

The following is a screenshot that shows the error in Visual Studio Code. 以下是显示Visual Studio Code中错误的屏幕截图。

在此处输入图片说明

Yes, you can do this. 是的,您可以这样做。 The problem you are probably having is that when you use a "fresh" object literal (that is, one that hasn't already been assigned to some variable) and assign it to a variable of a specified type, TypeScript uses excess property checks to warn you if you are adding unexpected properties. 您可能遇到的问题是,当您使用“新鲜”对象文字(即尚未分配给某个变量的对象)并将其分配给指定类型的变量时,TypeScript使用多余的属性检查来如果要添加意外属性,请警告您。 Types in TypeScript are normally treated as open/extendable/generalizable, but in this one instance they are treated as closed/ exact , because it's often an error to do this. TypeScript中的类型通常被视为open / extendable / generalizable,但是在这种情况下,它们被视为closed / exact ,因为这样做通常是错误的。

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

So, how can we prevent this from happening to us? 那么,如何防止这种情况发生在我们身上呢? All sorts of ways, in fact. 实际上,各种各样的方式。


One is to explicitly mention the subclasses we expect. 一种是明确提及我们期望的子类。 The type BaseClass and BaseClass | ChildClass1 | ChildClass2 | ChildClass3 类型BaseClassBaseClass | ChildClass1 | ChildClass2 | ChildClass3 BaseClass | ChildClass1 | ChildClass2 | ChildClass3 BaseClass | ChildClass1 | ChildClass2 | ChildClass3 are structurally identical, but they differ by how they see "extra" properties: BaseClass | 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

That would still throw an excess property error if you misspell operationType , so you might want to mention the types in some way. 如果您拼写operationType ,那仍然会抛出多余的属性错误,因此您可能需要以某种方式提及类型。 Still, having the type of your variable carry around the subclasses is kind of limiting. 尽管如此,让变量的类型携带子类仍然是一种限制。


Another way to do this is to assign the fresh object literal to variables annotated with the specific subclass type you care about, and then put those variables in your array: 执行此操作的另一种方法是将新鲜对象文字分配给使用您关心的特定子类类型注释的变量,然后将这些变量放入数组中:

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

That's one of the safest ways you can do this, since each variable childClass1 , childClass2 and childClass3 will be constrained and excess-property-checked to the relevant subclass. 这是您执行此操作最安全的方法之一,因为每个变量childClass1childClass2childClass3都将受到约束,并且会检查相关子类的属性是否过剩。


Another way you can do this is to completely turn off excess property checks by having the object literals no longer be "fresh". 您可以执行此操作的另一种方法是通过使对象文字不再“新鲜”来完全关闭多余的属性检查。 First, assign the array to an unannotated variable; 首先,将数组分配给未注释的变量; the type will be inferred by the compiler. 该类型将由编译器推断。 Then, assign the inferred-type variable to a BaseClass[] variable: 然后,将推断类型的变量分配给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

That works, but keep in mind that there will be absolutely no checking of anything except the BaseClass members. 那行得通,但是请记住,除了BaseClass成员外,将不会进行任何检查。 If you make operationType a number, it will still be accepted. 如果将operationType为数字,则仍然可以接受。


You could decide that you want BaseClass never to have excess property checks ever. 您可以决定BaseClass进行过多的属性检查。 One way to turn that completely off is to define a BaseClass extension which explicitly allows any excess properties of any type: 一种完全关闭该方法的方法是定义一个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

Finally, you might want to use a helper function to accept extensions of BaseClass (which turns off excess property checking): 最后,您可能想使用一个辅助函数来接受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

Hope one of those ways gives you some direction. 希望其中一种方法能给您一些指导。 Good luck! 祝好运!

Link to code 链接到代码

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM