Typescript 通过 in 运算符进行类型保护

标签 typescript types

我正要为此在 github 上发布一个问题,但我想我应该先在这里问。

我正在为我使用 typescript 编写的游戏开发组件实体系统。 javascript 和(据我所知)typescript 中的组件实体系统往往不是非常类型安全,我有一个想法来尝试解决这个问题。

我当前的计划是让每个依赖于多个组件的系统公开一个实体类型,该类型将具有该系统处理的实体应具有的许多属性。然后,我将在一个中心位置将所有各种实体类型收集到一个组合的实体联合类型中。

export type Entity =
    CameraManagerEntity |
    CollisionManagerEntity |
    HoleManagerEntity |
    ParentManagerEntity | ...

然后在系统中,我希望能够断言实体上有某些属性,并让 typescript 推断出,因为联合中唯一具有该属性的类型是我的系统导出的类型,那么它一定是导出的类型。

例如,导出 CameraManager

interface CameraManagerEntity {
    foo: boolean,
    bar: number
}

并且我的实体联合中没有其他类型具有 foo 参数。我的期望是,如果我有一个实体联合的实例,则制作一个断言“foo”位于该实例中的 if 语句应该足以访问该实例上的 bar 而不会出现类型错误。

function processEntity(entity: Entity) {
    if ("foo" in entity) {
        return entity.bar; // <-- Type error.
    }
}

我错过了什么吗?我认为在理想的情况下,编译器应该有足够的信息来知道我的实体对象是一个 CameraManagerEntity 。看来我的建议不起作用,因为今天存在 typescript 。有没有更好的方法来实现我想要做的事情?提前致谢。

编辑:我知道用户定义的类型保护,最好不必手动编写类型保护,因为我认为编译器应该已经拥有所有信息。

最佳答案

你想要的几乎就像 Tagged Union Type翻了个底朝天。使用标记联合类型,所有成员共享一个公共(public)字段,TypeScript 通过检查该字段的值(通常使用 switch)进行区分。但在您当前的设计中,联合类型的成员通过唯一字段的存在来区分。

我想出了一个通用类型防护,它使用属性的存在来检查实体与类型:

if (hasKeyOf<CameraManagerEntity>(entity, 'camera')) {
  return entity.camera;
}

这是完整的示例:

interface CameraManagerEntity {
  camera: string;
}

interface CollisionManagerEntity {
  collision: string;
}

type Entity = CameraManagerEntity | CollisionManagerEntity;

// Generic type guard 
function hasKeyOf<T>(entity: any, key: string): entity is T {
  return key in entity;
}

function processEntity(entity: Entity) {
  if (hasKeyOf<CameraManagerEntity>(entity, 'camera')) {
    return entity.camera;
  } else if (hasKeyOf<CollisionManagerEntity>(entity, 'collision')) {
    return entity.collision;
  } 
}

Try it in TypeScript Playground

但是您可能会考虑为您的实体系统使用标记联合类型,这可能更符合人体工程学:

interface CameraManagerEntity {
  kind: 'CameraManager';
  camera: string;
}

interface CollisionManagerEntity {
  kind: 'CollisionManager';
  collision: string;
}

type Entity = CameraManagerEntity | CollisionManagerEntity;

function processEntity(entity: Entity) {
  switch (entity.kind) {
    case ('CameraManager'): return entity.camera;
    case ('CollisionManager'): return entity.collision;
  }
}

Try it in TypeScript Playground

关于Typescript 通过 in 运算符进行类型保护,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/43422187/

相关文章:

typescript - <AG-Grid> 类型 'children' 上不存在属性 'ColDef<object>'

c# - 如何在 Angular POST 正文中包含 FormData 以将其作为 IFormFile 从 ASP.NET Core 后端检索

javascript - 在 TypeScript 中将字符串转换为枚举

python - QTableWidgetItem 返回项目类型(pyqt)

Mysql 二进制数据类型

c# - 在 C# 中保存相同类型的有序值对的最佳类型是什么?

reactjs - 如何解决错误: "You gave us a visitor for the node type TSSatisfiesExpression but it' s not a valid type"?

javascript - 您如何从从 index.html 导入的 Javascript 脚本访问全局变量和函数并在组件中使用它们?

haskell - 了解 Haskell 类型级别文字

java - 在Java中,表示枚举类型的字段常量的对象实例是否仅在首次访问时实例化?