Skip to content

Instantly share code, notes, and snippets.

@gund
Last active October 23, 2019 17:41
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save gund/2062d19bd8d604a6bd5c423aed6d66c2 to your computer and use it in GitHub Desktop.
Save gund/2062d19bd8d604a6bd5c423aed6d66c2 to your computer and use it in GitHub Desktop.
True Singleton Mixin
class B {
b: string;
}
class A extends asSingleton({ baseClass: B, ctorArgsFn: () => [] }) {
a: number;
}
class AsyncA extends asSingletonAsync({
baseClass: B,
ctorArgsFn: () => Promise.resolve([])
}) {
a: number;
}
const a = A.get();
console.log(a.b);
AsyncA.get().then(a => console.log(a.b));
interface AbstractClass<T = any> {
prototype: T;
}
interface Class<T = any> extends AbstractClass<T> {
new (...args: any[]): T;
}
export class SingletonClassError extends Error {
constructor(ctorName: string = 'Class') {
super(`${ctorName} is singleton!`);
}
}
export interface AsSingletonOptions<B extends Class, A = any[]> {
baseClass?: B;
ctorArgsFn?: () => A;
}
export interface AsSingletonAsyncOptions<B extends Class>
extends AsSingletonOptions<B, Promise<any[]>> {}
export function asSingleton<B extends Class>({
baseClass = class {} as B,
ctorArgsFn = () => []
}: AsSingletonOptions<B>) {
let instance = null;
let constructing = false;
return class Singleton extends baseClass {
static get<T>(this: { prototype: T }): T {
if (!instance) {
constructing = true;
instance = new (this as any)(...ctorArgsFn());
constructing = false;
}
return instance;
}
constructor(...args) {
if (!constructing) {
throw new SingletonClassError();
}
super(...args);
}
};
}
export function asSingletonAsync<B extends Class>({
baseClass,
ctorArgsFn
}: AsSingletonAsyncOptions<B>) {
let instance = null;
let constructing = false;
return class Singleton extends baseClass {
static async get<T>(this: { prototype: T }): Promise<T> {
if (!instance) {
const args = ctorArgsFn ? await ctorArgsFn() : [];
constructing = true;
instance = new (this as any)(...args);
constructing = false;
}
return instance;
}
constructor(...args) {
if (!constructing) {
throw new SingletonClassError();
}
super(...args);
}
};
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment