打字稿可变参数泛型类,接受扩展公共基础的可变数量的类对象?

如何解决打字稿可变参数泛型类,接受扩展公共基础的可变数量的类对象?

我正在尝试用打字稿设计一个实体组件系统。我的目标是创建一个可变参数泛型抽象 SomeClass(B) 类,它将组件类作为泛型参数。

这个想法是派生的 System 类可以用它们使用的组件来声明。然后,他们可以使用此信息(组件类对象)从所有实体的列表中提取他们操作的实体子集。该子集是具有所需组件的实体集。

对于上下文,我首先包含了组件和实体代码:

System

我试图使它成为一个可变参数泛型,可以采用任意数量的组件 类,每个类都扩展 interface IComponent { owner: Entity | null; } type ComponentClass<C extends IComponent> = new (args: unknown[]) => C; abstract class Entity { public readonly name: string; /** The pixijs scene node (pixijs display object). */ private _sceneNode: unknown; protected _components: IComponent[] = []; constructor(name: string) { this.name = name; } public get components(): IComponent[] { return this._components; } public addComponent(component: IComponent): void { this._components.push(component); component.owner = this; } public getComponent<C extends IComponent>(componentClass: ComponentClass<C>): C { for (const component of this._components) { if (component instanceof componentClass) { return component as C; } } throw new Error(`component '${componentClass.name}' missing from entity ${this.constructor.name}`); } public removeComponent<C extends IComponent>(componentClass: ComponentClass<C>): void { const removeList: number[] = []; this._components.forEach((component,index) => { if (component instanceof componentClass) { removeList.push(index); } }); removeList.forEach(index => { this._components.splice(index,1); }); } public hasComponent<C extends IComponent>(componentClass: ComponentClass<C>): boolean { return this._components.some(component => { return component instanceof componentClass; }); } } interface ISystem { onSpawnEntity(entity: Entity): void; onDestroyEntity(entity: Entity): void; pullEntities(entities: Entity[]): void; update(dt_s: number) : void; } 。目前,它只需要 2,这说明了我想要实现的目标:

IComponent

我还包含了示例用法以提供更多上下文:

abstract class System<C0 extends IComponent,C1 extends IComponent> implements ISystem {
    protected _targets: [ComponentClass<C0>,ComponentClass<C1>];
    protected _subjects: Entity[] = [];

    constructor(targets: [ComponentClass<C0>,ComponentClass<C1>]) {
        this._targets = targets;
    }

    public pullEntities(entities: Entity[]): void {
        entities.forEach(entity => {
            if(this.isEntitySubject(entity)) {
                this._subjects.push(entity);
            }
        });
    }

    public onSpawnEntity(entity: Entity): void {
        if(this.isEntitySubject(entity)) {
            this._subjects.push(entity);
        }
    }

    public onDestroyEntity(entity: Entity): void {
        if(this.isEntitySubject(entity)) {
        }
    }

    public update(dt_s: number) : void {
        this._subjects.forEach(entity => {
            const c0 = entity.getComponent(this._targets[0]);
            const c1 = entity.getComponent(this._targets[1]);
            this.updateEntity(c0,c1);
        })
    }

    private isEntitySubject(entity: Entity): boolean {
        return entity.hasComponent(this._targets[0]) &&
            entity.hasComponent(this._targets[1]);
    }

    // the idea is that this is the only function systems will have to implement themselves,// ideally want the args to be a variadic array of the component instances which the
    // system uses.
    protected updateEntity(c0: C0,c1: C1) {}
}

abstract class World
{
    protected _systems: ISystem[] = [];
    protected _entities: Entity[] = [];

    public feedEntities(): void {
        this._systems.forEach(system => {
            system.pullEntities(this._entities);
        });
    }

    public updateSystems(): void {
        this._systems.forEach(system => {
            system.update(20);
        });
    }
}

我正在努力实现的目标是否可能?

解决方法

TypeScript 没有“可变泛型”,但它有 Tuple types

所以你的 System 类可以

type ComponentClasses<T extends IComponent[]> = { [K in keyof T]: T[K] extends IComponent ? ComponentClass<T[K]> : never };

abstract class System<TComponents extends IComponent[]> implements ISystem {
    protected _targets: ComponentClasses<TComponents>;
    protected _subjects: Entity[] = [];

    constructor(targets: ComponentClasses<TComponents>) {
        this._targets = targets;
    }

    protected abstract updateEntity(...components: TComponents): void;
}

说明:

  1. TComponents extends IComponent[] 表示 TComponent 必须是 IComponent 的数组(或元组),例如 PhysicsComponent[][PhysicsComponent,CollisionComponent](我没有测试,但我代码的其他部分应该只适用于元组类型)
  2. 为了将 TComponents 转换为它们的 ComponentClass,我使用了一个辅助类型 ComponentClasses,它是一个 Mapped type,特别是,映射元组类型仅映射其数字键,表示 ComponentClasses<[PhysicsComponent,CollisionComponent]> 将返回 [ComponentClass<PhysicsComponent>,ComponentClass<CollisionComponent>]
  3. 为了使 updateEntity 方法接受可变数量的参数,使用了 Rest Parameters 语法。在 TypeScript 中,它允许使用元组类型标记多个参数。

PhysicsSystem 的示例:

class PhysicsSystem extends System<[PhysicsComponent,CollisionComponent]> {
    protected override updateEntity(physics: PhysicsComponent,collision: CollisionComponent) {
        physics.x += 1;
        physics.y += 1;

        console.log(`entity: ${physics.owner!.name} has position: {x: ${physics.x},y: ${physics.y}}`);
    }
}

如果更改 physicscollision 参数的类型,它将无法编译。

GameWorld中:

this._systems.push(new PhysicsSystem([PhysicsComponent,CollisionComponent]));

如果更改参数数组,它也不会编译。

版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。

相关推荐


Selenium Web驱动程序和Java。元素在(x,y)点处不可单击。其他元素将获得点击?
Python-如何使用点“。” 访问字典成员?
Java 字符串是不可变的。到底是什么意思?
Java中的“ final”关键字如何工作?(我仍然可以修改对象。)
“loop:”在Java代码中。这是什么,为什么要编译?
java.lang.ClassNotFoundException:sun.jdbc.odbc.JdbcOdbcDriver发生异常。为什么?
这是用Java进行XML解析的最佳库。
Java的PriorityQueue的内置迭代器不会以任何特定顺序遍历数据结构。为什么?
如何在Java中聆听按键时移动图像。
Java“Program to an interface”。这是什么意思?
Java在半透明框架/面板/组件上重新绘画。
Java“ Class.forName()”和“ Class.forName()。newInstance()”之间有什么区别?
在此环境中不提供编译器。也许是在JRE而不是JDK上运行?
Java用相同的方法在一个类中实现两个接口。哪种接口方法被覆盖?
Java 什么是Runtime.getRuntime()。totalMemory()和freeMemory()?
java.library.path中的java.lang.UnsatisfiedLinkError否*****。dll
JavaFX“位置是必需的。” 即使在同一包装中
Java 导入两个具有相同名称的类。怎么处理?
Java 是否应该在HttpServletResponse.getOutputStream()/。getWriter()上调用.close()?
Java RegEx元字符(。)和普通点?