【问题标题】:Use Mixins with union types in Typescript在 Typescript 中使用 Mixins 和联合类型
【发布时间】:2019-08-28 23:14:31
【问题描述】:

我有一个简单的系统,我通过从不同的基类继承它们来生成类,然后将另一个类混合到每个基类中。这是我的 mixin 类:

type Constructor<T = {}> = new (...args: any[]) => T;

/**
 * Based on the Mixin idea explained here:
 * https://mariusschulz.com/blog/typescript-2-2-mixin-classes
 *
 * @param base
 * @constructor
 */
export function EntityServices<TBase extends Constructor>(base: TBase) {
  return class extends base {
    private _components = {};

    public addComponent(component: Component) {
      throw new Error('Not implemented');
    }

    public removeComponent(component: Component) {
      throw new Error('Not implemented');
    }
  };
}

这个 mixin 在另一个模块中被用来创建几个像这样的类:

class ContainerEntityBase extends Phaser.GameObjects.Container {}
class ImageEntityBase extends Phaser.GameObjects.Image {}
class SpriteEntityBase extends Phaser.GameObjects.Sprite {}
class TextEntityBase extends Phaser.GameObjects.Text {}

export const ContainerEntity = EntityServices(ContainerEntityBase);
export const ImageEntity = EntityServices(ImageEntityBase);
export const SpriteEntity = EntityServices(SpriteEntityBase);
export const TextEntity = EntityServices(TextEntityBase);

// Type definitions have to be exported separately so that they can be used as types elsewhere, not as values
// Same name with values (classes) does not matter since TS stores values and types into separate
// namespaces.
export type ContainerEntity = InstanceType<typeof ContainerEntity>;
export type ImageEntity = InstanceType<typeof ImageEntity>;
export type SpriteEntity = InstanceType<typeof SpriteEntity>;
export type TextEntity = InstanceType<typeof TextEntity>;
export type BlackbirdEntity = ContainerEntity | ImageEntity | SpriteEntity | TextEntity;

如您所见,我已经使用一个额外的联合类型BlackBirdEntity 导出了实际创建的类及其类型。有时我会使用可能是任何生成类型的变量,因为在这些情况下,这些实例是由它们共同的混合接口操作的。

接下来我有以下使用联合类型的简单定义:

import { Component } from '../core/Component';
import { BlackbirdEntity } from '../core/entities';

export interface IEntityDefinition {
  name: string;
  components: Component[];
  type: BlackbirdEntity;
}

我用它来创建一个实现上述接口的对象:

import { SpriteEntity } from '../core/entities';
import { IEntityDefinition } from './EntityDefinition';

const clickableEntity: IEntityDefinition = {
  components: [],
  name: 'Clickable',
  type: SpriteEntity
};

但是,这给了我在 IDE 上的以下错误,并突出显示了 SpriteEntity

TS2322: Type '{ new (...args: any[]): EntityServices<typeof SpriteEntityBase>.(Anonymous class); prototype: EntityServices<any>.(Anonymous class); } & typeof SpriteEntityBase' is not assignable to type 'BlackbirdEntity'.   Type '{ new (...args: any[]): EntityServices<typeof SpriteEntityBase>.(Anonymous class); prototype: EntityServices<any>.(Anonymous class); } & typeof SpriteEntityBase' is not assignable to type 'EntityServices<typeof TextEntityBase>.(Anonymous class) & TextEntityBase'.     Type '{ new (...args: any[]): EntityServices<typeof SpriteEntityBase>.(Anonymous class); prototype: EntityServices<any>.(Anonymous class); } & typeof SpriteEntityBase' is missing the following properties from type 'EntityServices<typeof TextEntityBase>.(Anonymous class)': _components, addComponent, removeComponent

为什么?以及如何解决这个问题?该错误似乎表明SpriteEntity 缺少TextEntity 的父类中实际存在的属性。那么有没有办法告诉编译器这种类型应该没问题,即使他们父母的定义不同?

【问题讨论】:

  • SpriteEntity 没有类型 SpriteEntity。您的意思是 IEntityDefinition['type']Constructor&lt;BlackbirdEntity&gt; 或者您的意思是将 SpriteEntity 类型的 instance 作为 typetype 属性clickableEntity
  • 是的,我的意思是将SpriteEntity 的一个实例作为typetype 属性。我如何用 TS 类型的 defs 来表达它?或者我如何将 type SpriteEntity 附加到 value SpriteEntity
  • 澄清一下,我想将 SpriteEntity 设置为 clickableEntity 的属性 type。而不是 type 属性应该是 entityClass 等,因为我希望它引用一个稍后实例化的类。 clickableEntity 对象是工厂的配置对象。
  • 所以你想在IEntityDefinition的定义中使用Constructor&lt;BlackbirdEntity&gt;而不是BlackbirdEntity

标签: typescript types mixins unions


【解决方案1】:

您的问题是IEntityDefinition 希望其type 属性是BlackbirdEntity实例,而不是构造函数。您可能会感到困惑,因为对于 class,构造函数 value 通常与实例 type 共享一个名称,即使 they are not the same thing 也是如此。

不管怎样,你已经有了一个 Constructor&lt;T&gt; 类型的别名,所以让我们使用它吧:

export interface IEntityDefinition {
  name: string;
  components: Component[];
  type: Constructor<BlackbirdEntity>; // you want a constructor, not an instance here
}

这应该使您的clickableEntity 变量初始化编译没有错误。

希望有所帮助;祝你好运!

【讨论】:

  • 啊,这绝对有道理!由于某种原因,我没有明白我必须将类型指定为构造函数并意外使用了一个实例。谢谢!
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2021-04-11
  • 2017-07-18
  • 1970-01-01
  • 1970-01-01
  • 2020-10-15
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多