在通用 Typescript 类中实现单例

Chr*_*lla 3 generics singleton design-patterns typescript

我有一个通用类型,它将作为事件系统中事件的基类。我们希望它们是单例,但它也是通用的,以便子类可以在事件触发时指定他们在回调中期望的参数类型。

我所拥有的看起来像这样:

export interface IEvent { }
export interfact IEventArg { }

export abstract class Event<TEvent extends IEvent, TEventArg extends IEventArg> implements IMessageHandler {

private static s_instance : any;
private static s_isInitializing : boolean;

constructor() {
    if (!Event.s_isInitializing) 
        throw new Error ("Use the GetInstance method to get this class instance.");
}

public static GetInstance<TEvent extends IEvent, TEventArg extends IEventArg>(type: { new() : T; }) : TEvent {
    if (!s_instance) {
        s_isInitializing = true;
        s_instance = new type();
        s_isInitializing = false;
    }

    return s_instance;
}
}

export class SelectionEvent extends Event<SelectionEvent, EmptyEventArg> {
...
}

var event = SelectionEvent.GetInstance(SelectionEvent);
Run Code Online (Sandbox Code Playgroud)

这里有一些东西我认为很奇怪,主要是因为 Typescript 语言的要求。GetInstance 的参数似乎是必要的,因为var x = new TEvent()TypeScript 似乎不允许。空接口有助于强制执行可以接受为泛型类型的内容。

这里似乎不起作用的是静态变量和泛型类型的组合。GetInstance 方法中的块是我想要做的,但显然它无法编译。我还担心静态实例变量不会为每个事件创建一次,而是仅为每个事件实例创建一次,因此会被覆盖。

这里的任何指导将不胜感激。

Rya*_*ugh 5

在 C# 中,泛型类的每个实例化(类型参数集)都有自己的静态成员集。这意味着您可以编写一种单例模式,其中有一个 for 实例Foo<int>,另一个 for 实例Foo<string>,依此类推。这是可能的,因为泛型是在运行时显现的——运行时系统实际上知道泛型是什么,并且可以为每个类型实例化分配新对象。此外,类的静态端可以引用该类的类型参数(这是运行时语义的结果)。

在 TypeScript 中,情况并非如此。每个类只有一个构造函数,无论是泛型还是其他类。这意味着类的静态部分无法“看到”泛型类型参数,因为对于任何类X<T>具有静态成员的类y,只有一个运行时槽X.y

类的单例模式通常不太适合 TypeScript;请参阅如何在 TypeScript 中定义 Singleton。我想你有一些XY 问题,因此如果没有有关您的用例的更多信息(也许发布一个单独的问题),我无法推荐任何具体的替代方案。

另外,TypeScript 中永远不应该有空接口。类型在结构上进行比较,因此任何两个空类型都是兼容的,并且您可以将任何内容分配给类型为空接口的变量。