Remember to enable experimentalDecorators in your tsconfig.json !
Last active
June 13, 2018 22:44
-
-
Save 422404/e3a6f56e107ad8a5d51ee7a815f2ec91 to your computer and use it in GitHub Desktop.
Singletonify™ a class using Typescript decorators
This file contains hidden or 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
import Singleton from './singletonify' | |
@Singleton() | |
class MyStrictSingleton {} | |
@Singleton(false) | |
class MySingleton {} | |
console.log('MyStrictSingleton instanciation 1:'); | |
try { | |
let strictSingleton1 = new MyStrictSingleton(); | |
console.log('No error on instanciation - OK'); | |
} catch (e) { | |
console.log('Error on instanciation - NOK'); | |
} | |
console.log('MyStrictSingleton instanciation 2:'); | |
try { | |
let strictSingleton2 = new MyStrictSingleton(); | |
console.log('No error on instanciation - NOK'); | |
} catch (e) { | |
console.log('Error on instanciation - OK'); | |
} | |
console.log('\nMySingleton instanciation 1:'); | |
let singleton1; | |
let singleton2 | |
try { | |
singleton1 = new MySingleton(); | |
console.log('No error on instanciation - OK'); | |
} catch (e) { | |
console.log('Error on instanciation - NOK'); | |
} | |
console.log('MySingleton instanciation 2:'); | |
try { | |
singleton2 = new MySingleton(); | |
console.log('No error on instanciation - OK'); | |
} catch (e) { | |
console.log('Error on instanciation - NOK'); | |
} | |
console.log('MySingleton instance comparison:'); | |
if (singleton1 === singleton2) { | |
console.log('Same reference - OK'); | |
} else { | |
console.log('Differents references - NOK'); | |
} |
This file contains hidden or 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
/******************* Typings *******************/ | |
/** | |
* Represents a constructible class | |
*/ | |
interface IClass { | |
new(...args: any[]): {} | |
} | |
/******************* Private *******************/ | |
/** | |
* Modifies a class in a manner that it can be instanciated only once | |
* Any other "new" will throw an exception | |
* @param {IClass} constructor Class to strictly singletonify | |
* @returns {IClass} Singletonified class | |
*/ | |
function strictlySingletonify(constructor: IClass): IClass { | |
return (function (klass: IClass) { | |
let SingletonifiedKlass = class extends klass { | |
static $$instanciatedOnce: boolean = false; | |
static $$type: string = 'strict-singleton'; | |
constructor(...args: any[]) { | |
if (SingletonifiedKlass.$$instanciatedOnce) { | |
throw 'Cannot instanciate singleton class twice !'; | |
} | |
SingletonifiedKlass.$$instanciatedOnce = true; | |
super(...args); | |
} | |
} | |
return SingletonifiedKlass; | |
})(constructor); | |
} | |
/** | |
* Modifies a class in a manner that it can be instanciated only once | |
* Any other "new" will return the same first instance | |
* @param {IClass} constructor Class to strictly singletonify | |
* @returns {IClass} Singletonified class | |
*/ | |
function singletonify(constructor: IClass): IClass { | |
return (function (klass: IClass) { | |
let SingletonifiedKlass = class extends klass { | |
static $$instance: Object = null; | |
static $$type: string = 'singleton'; | |
constructor(...args: any[]) { | |
if (SingletonifiedKlass.$$instance) { | |
return SingletonifiedKlass.$$instance; | |
} | |
super(...args); | |
SingletonifiedKlass.$$instance = this; | |
} | |
} | |
return SingletonifiedKlass; | |
})(constructor); | |
} | |
/******************* Public ********************/ | |
/** | |
* Decorates a class in a manner it cannot be instanciated twice | |
* making it a true singleton | |
* @param {boolean} strict Whether or not the "new" syntax makes it throw an exception | |
* true: if the class is beeing instanciated a second time it throws an exeption | |
* flase : the "new" syntax just returns the first instance | |
* Defaults to true | |
*/ | |
export default function Singleton(strict: boolean = true): Function { | |
return function (constructor: IClass): IClass { | |
if (strict) { | |
return strictlySingletonify(constructor); | |
} | |
return singletonify(constructor); | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment