Class 和 属性,其中 属性 本身包含类型 <T extends Class> 的 属性
Class with property, where the property itself contains a property of type <T extends Class>
我正在写一个 Engine
class,它在构造时采用 Module
classes 的映射并实例化它们通过自身。然后将创建的实例存储在 modules
中。我使用泛型来确保引擎上存在的模块是已知的,并且可以被采用 Engine
.
类型参数的函数声明为预期的
此 Engine
class 还包含地图事件处理程序,它们需要一个将 Engine
实例作为其第一个参数的回调。可以通过 public 方法注册处理程序。
模块可能如下所示:
class Renderer extends Module<{Renderer: Renderer}> {
constructor(engine: Engine<{Renderer: Renderer}>) {
super(engine);
this.engine.addEventHandler('draw', drawSomething);
}
renderText(message: string) {
console.log(message);
}
}
... 其中 drawSomething
,事件处理程序,看起来像这样:
function drawSomething(engine: Engine<{Renderer: Renderer}>): void {
engine.modules.Renderer.renderText('hello world');
}
你会发现整个问题都解决了 here in TypeScript playground, with the error at line 54. I've also created one with only the type definitions,看看我想要的概念的形状。
我遇到的问题是,当我注册事件处理程序时drawSomething
,在检查处理程序是否正确键入时,我传递的泛型似乎被遗忘了。假设处理程序将在没有 Renderer
作为已知模块的情况下被调用。打字稿告诉我:
Type 'Engine<{}>' is not assignable to type 'Engine<{ Renderer: Renderer; }>'
我如何确保 TypeScript 知道当我 向具有某些模块的 Engine
实例注册 事件处理程序时,处理程序也将是 在调用时传递了与那些模块相同的Engine
实例?
我正在使用 TypeScript 3.5.3。
关键问题是 Module
的递归类型定义,它接受必须是其自身子类型的另一种类型。
原代码
class Module<TModulesOfEngine extends Modules = {}> {
engine: Engine<TModulesOfEngine>;
constructor(engine: Engine<TModulesOfEngine>) {
this.engine = engine;
}
}
class Engine<TModules extends Modules = {}> {
public modules: TModules;
public eventHandlers: {
[eventName: string]: EventHandler<TModules>[];
} = {};
constructor(modules: ConstructableModules<TModules> = {} as ConstructableModules<TModules>) {
this.modules = Object.keys(modules).reduce((allModules: TModules, moduleName: string): TModules => {
const ModuleClass = modules[moduleName];
return {
...allModules,
[moduleName]: new ModuleClass(this),
}
}, {} as TModules);
}
...
在某些情况下,polymorphic this
type could be used, as shown in this example:
但是,this
类型不能用在静态方法或构造函数中,也就是说下面是错误的。
class Module {
engine: Engine<this>;
// Error: A 'this' type is available only in a non-static member of a class or interface.
constructor(engine: Engine<this>) {
this.engine = engine;
}
}
虽然不美观,但下面的递归类型定义是可行的。
解决方案
class Module<T extends Module<T>> {
engine: Engine<T>;
constructor(engine: Engine<T>) {
this.engine = engine;
}
}
class Engine<T extends Module<T>> {
public modules: ModuleMap<T>;
public eventHandlers: {
[eventName: string]: EventHandler<T>[];
} = {};
constructor(modules: ConstructableModule<ModuleMap<T>>) {
this.modules = Object.keys(modules).reduce((allModules: ModuleMap<T>, moduleName: string): ModuleMap<T> => {
const ModuleClass = modules[moduleName];
return {
...allModules,
[moduleName]: new ModuleClass(this),
}
}, {} as ModuleMap<T>);
}
addEventHandler(name: string, handler: EventHandler<T>): void {
this.eventHandlers = {
...this.eventHandlers,
[name]: [
...(this.eventHandlers[name] || []),
handler,
],
};
}
}
参见 Typescript Playground 上的工作示例。
Modules
接口的原始定义打破了泛型链。对它的任何使用都会使 TypeScript 失去先前定义的类型 Engine.modules
。由于它不接受任何传递给 Module
的泛型,因此无法在 Engine
:
上捕获预期的模块
class Module<TModulesOfEngine extends Modules = {}> {
engine: Engine<TModulesOfEngine>;
constructor(engine: Engine<TModulesOfEngine>) {
this.engine = engine;
}
}
// This is missing generic arguments, making it impossible to preserve typing.
interface Modules {
[moduleName: string]: Module;
}
要解决此问题,Modules
也需要开始接受 TModulesOfEngine
:
class Module<TModulesOfEngine extends Modules<TModulesOfEngine> = {}> {
engine: Engine<TModulesOfEngine>;
constructor(engine: Engine<TModulesOfEngine>) {
this.engine = engine;
}
}
interface Modules<TModulesOfEngine extends Modules<TModulesOfEngine> = {}> {
[moduleName: string]: Module<TModulesOfEngine>;
}
可以在 this TypeScript playground 中找到完整的实现。
我通过删除类型默认值(例如 TModules extends Modules = {}
)找到了答案,它揭示了忘记传递泛型的地方。
我正在写一个 Engine
class,它在构造时采用 Module
classes 的映射并实例化它们通过自身。然后将创建的实例存储在 modules
中。我使用泛型来确保引擎上存在的模块是已知的,并且可以被采用 Engine
.
此 Engine
class 还包含地图事件处理程序,它们需要一个将 Engine
实例作为其第一个参数的回调。可以通过 public 方法注册处理程序。
模块可能如下所示:
class Renderer extends Module<{Renderer: Renderer}> {
constructor(engine: Engine<{Renderer: Renderer}>) {
super(engine);
this.engine.addEventHandler('draw', drawSomething);
}
renderText(message: string) {
console.log(message);
}
}
... 其中 drawSomething
,事件处理程序,看起来像这样:
function drawSomething(engine: Engine<{Renderer: Renderer}>): void {
engine.modules.Renderer.renderText('hello world');
}
你会发现整个问题都解决了 here in TypeScript playground, with the error at line 54. I've also created one with only the type definitions,看看我想要的概念的形状。
我遇到的问题是,当我注册事件处理程序时drawSomething
,在检查处理程序是否正确键入时,我传递的泛型似乎被遗忘了。假设处理程序将在没有 Renderer
作为已知模块的情况下被调用。打字稿告诉我:
Type 'Engine<{}>' is not assignable to type 'Engine<{ Renderer: Renderer; }>'
我如何确保 TypeScript 知道当我 向具有某些模块的 Engine
实例注册 事件处理程序时,处理程序也将是 在调用时传递了与那些模块相同的Engine
实例?
我正在使用 TypeScript 3.5.3。
关键问题是 Module
的递归类型定义,它接受必须是其自身子类型的另一种类型。
原代码
class Module<TModulesOfEngine extends Modules = {}> {
engine: Engine<TModulesOfEngine>;
constructor(engine: Engine<TModulesOfEngine>) {
this.engine = engine;
}
}
class Engine<TModules extends Modules = {}> {
public modules: TModules;
public eventHandlers: {
[eventName: string]: EventHandler<TModules>[];
} = {};
constructor(modules: ConstructableModules<TModules> = {} as ConstructableModules<TModules>) {
this.modules = Object.keys(modules).reduce((allModules: TModules, moduleName: string): TModules => {
const ModuleClass = modules[moduleName];
return {
...allModules,
[moduleName]: new ModuleClass(this),
}
}, {} as TModules);
}
...
在某些情况下,polymorphic this
type could be used, as shown in this example:
但是,this
类型不能用在静态方法或构造函数中,也就是说下面是错误的。
class Module {
engine: Engine<this>;
// Error: A 'this' type is available only in a non-static member of a class or interface.
constructor(engine: Engine<this>) {
this.engine = engine;
}
}
虽然不美观,但下面的递归类型定义是可行的。
解决方案
class Module<T extends Module<T>> {
engine: Engine<T>;
constructor(engine: Engine<T>) {
this.engine = engine;
}
}
class Engine<T extends Module<T>> {
public modules: ModuleMap<T>;
public eventHandlers: {
[eventName: string]: EventHandler<T>[];
} = {};
constructor(modules: ConstructableModule<ModuleMap<T>>) {
this.modules = Object.keys(modules).reduce((allModules: ModuleMap<T>, moduleName: string): ModuleMap<T> => {
const ModuleClass = modules[moduleName];
return {
...allModules,
[moduleName]: new ModuleClass(this),
}
}, {} as ModuleMap<T>);
}
addEventHandler(name: string, handler: EventHandler<T>): void {
this.eventHandlers = {
...this.eventHandlers,
[name]: [
...(this.eventHandlers[name] || []),
handler,
],
};
}
}
参见 Typescript Playground 上的工作示例。
Modules
接口的原始定义打破了泛型链。对它的任何使用都会使 TypeScript 失去先前定义的类型 Engine.modules
。由于它不接受任何传递给 Module
的泛型,因此无法在 Engine
:
class Module<TModulesOfEngine extends Modules = {}> {
engine: Engine<TModulesOfEngine>;
constructor(engine: Engine<TModulesOfEngine>) {
this.engine = engine;
}
}
// This is missing generic arguments, making it impossible to preserve typing.
interface Modules {
[moduleName: string]: Module;
}
要解决此问题,Modules
也需要开始接受 TModulesOfEngine
:
class Module<TModulesOfEngine extends Modules<TModulesOfEngine> = {}> {
engine: Engine<TModulesOfEngine>;
constructor(engine: Engine<TModulesOfEngine>) {
this.engine = engine;
}
}
interface Modules<TModulesOfEngine extends Modules<TModulesOfEngine> = {}> {
[moduleName: string]: Module<TModulesOfEngine>;
}
可以在 this TypeScript playground 中找到完整的实现。
我通过删除类型默认值(例如 TModules extends Modules = {}
)找到了答案,它揭示了忘记传递泛型的地方。