【发布时间】:2021-02-21 04:11:15
【问题描述】:
我正在编写一个库,用户可以在其中定义抽象类State 的子类。 State 的任何子类将“依赖”(在库的逻辑内)从抽象类 Component 继承的许多类。这些依赖项必须在代码中静态声明,以便我的库可以预先分析它们。我还想根据 State-inherited 用户类定义的依赖项对它们的方法进行类型检查。这是一个例子:
class ComponentA extends Component {}
class ComponentB extends Component {}
class ExampleState extends State {
static dependencies = [ComponentA, ComponentB] as const;
// this method should correctly type the tuple provided to this
// user-defined method as containing an instance of ComponentA
// in position 0 and an instance of ComponentB in position 1, ordered as
// provided in the static dependencies property.
initialize(components) {
const [a, b] = components;
console.assert(a instanceof ComponentA); // true
}
}
具体来说,我想避免强迫用户也将泛型传递给State,比如State<[typeof ComponentA, typeof ComponentB]>,这既冗长又多余。但是,我不能仅依赖泛型,因为我在运行时需要依赖信息。
有没有在 TypeScript 中定义这个的选项?还是静态数据与实例太分离而无法共享这种类型的信息?是否有另一种模式(可能除了类)能够为这种用法实现正确的开发人员人体工程学?
更新
经过大量实验,我发现了至少一种可能的用法,它可以保持人体工程学(根据指定的依赖项在initialize 方法上键入提示),而不需要代码中的冗余:
不过,用法还是有点别扭。用户必须提供一个函数来创建他们的伪类对象,并利用一个闭包来存储他们通常会简单地附加到他们的子类的“实例属性”。
我仍然有兴趣看看是否有人可以提出更简化的解决方案,但如果没有,我至少会自己回答这个问题。
更新 2:关于我的目标的更多详细信息
我正在开发一个用于创建网页游戏的框架库,其灵感来自/扩展了用于游戏开发的 ECS 模式。我想添加到 ECS 的部分是框架用户或补充库作者根据附加到任何特定实体的某些组件组合定义声明性状态管理的一种方式。
作为使用框架编写游戏的用户,我想启用这样的模式:“如果任何实体在游戏生命周期的任何时间点都附有组件 [Image, Position, Color],Sprite "State" 对象应该由从这些组件中派生数据的框架构建。”
为此,用户将向实现initialize/destroy 接口的框架提供状态定义。这些状态定义将负责提供命令式逻辑以在游戏的逻辑代码中启用声明式使用。
这是我理想情况下想要启用的使用草图,使用类似于 PixiJS 或 ThreeJS 的假设 SomeSpriteThing 资源,例如:
class SpriteState extends State {
static dependencies = [Image, Position, Color] as const;
private sprite = new SomeSpriteThing();
initialize([image, position, color]) {
this.sprite.src = image.src;
this.sprite.x = position.x;
this.sprite.y = position.y;
this.sprite.tint = color.value;
this.sprite.load();
}
destroy() {
this.sprite.unload();
}
}
库用户会向框架提供此类 State 类,并且每当将一个实体分配给 [Image, Position, Color] 组件时,框架将生成一个 SpriteState 并提供给游戏逻辑代码。
但是,在上述理想用法中,initialize 无法从 dependencies 静态推断所提供元组的类型。
作为一个专注于 Typescript 的库,我希望对用户编写的代码进行彻底的类型检查,而不需要他们在指定冗余类型方面付出过多的努力。这就是为什么我想避免强迫用户为 State 超类中的泛型参数提供相同的依赖类型列表 - 这不仅不方便,而且没有办法强制 dependencies 和generic 保持同步,因为一个只对代码可见,另一个对类型检查可见。
我认识到这不是我以前见过的模式,并且可能是过度设计的。也许这就是它的全部。但我很好奇是否有某种可能的方式来处理类型安全。
【问题讨论】:
标签: typescript typescript-generics