打字稿型后卫运营商

打字稿型后卫运营商

问题描述:

我正要张贴在GitHub上的这个问题,但我想我会先问这里。打字稿型后卫运营商

我工作的一个游戏,我使用打字稿编写组件实体系统上。在javascript中的组件实体系统(据我所知)打字稿往往不是非常安全的类型,我有一个想法来尝试弥补这一点。

我目前的计划是让每个依赖于多个组件的系统暴露,这将具有许多哪些实体,该系统进程应该有特性的实体类型。然后,我将在一个中心位置将所有各种实体类型收集到一个组合的实体联合类型中。

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

然后在一个系统中,我希望能够断言某些属性的实体,并有打字稿推断,因为其中有工会的唯一类型,关于它的属性是类型我的系统已导出,那么它必须是导出的类型。

例如,说CameraManager出口

interface CameraManagerEntity { 
    foo: boolean, 
    bar: number 
} 

在我的实体联盟没有其他类型都有一个Foo参数。我的期望是,如果我有一个实体联合的实例,那么在实例中创建一个声明“foo”的if语句应该足以访问实例上的栏,而不会出现类型错误。

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

我错过了什么吗?我认为,在一个理想的世界中,编译器应该有足够的信息来知道我的实体对象是来自它的CameraManagerEntity。看来,我所建议的今天不存在打字稿。有没有更好的方法来实现我想要做的事情?提前致谢。

编辑:我知道用户定义类型后卫,这纯粹是不错的没有,因为我认为编译器应该已经有了所有这些信息来进行手动写入型后卫。

你想要的是几乎像一个Tagged Union Type原来由内而外。带标签的联盟类型,所有成员都有一个共同的领域,打字稿判断通过检查字段的值(通常switch)。但在你目前的设计中,联合类型的成员通过独特的字段来区分。

我想出了一个通用型后卫,使用属性的存在检查的entity针对类型:

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

+0

我落得这样做非常类似的措施。我仍然相信一个足够聪明的编译器应该能够找出正在使用的类型,或者至少缩小基于in运算符的联合中的类型,但是现在写入类型的守卫就足够了。感谢您的回答。 – Devagamster