Last active
October 23, 2019 17:41
-
-
Save gund/2062d19bd8d604a6bd5c423aed6d66c2 to your computer and use it in GitHub Desktop.
True Singleton Mixin
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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)); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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