argument This npm library enables users to create decorators that run when the class constructs.
$ npm install instance-decorators
Pages (or skip)
There are a huge range of applicable reasons and places where using this library benifits you (and your library/frameworks's users) including..
function database(name?: string): typeof InstanceDecorator {
return Instance(function(values: Values, variableName: string) {
values[variableName] = db.get(name ?? variableName)
})
}
@Instance()
class Values {
@database("value")
value: string
}
const FreezedBind = Instance(function(values: Values, name: string, descriptor: PropertyDescriptor) {
const valuesClone = structuredClone(values)
Object.freeze(valuesClone)
values[name].bind(valuesClone)()
})
@Instance()
class Values {
@FreezedBind
method() {
...
}
}
function Process<T>(processor: ((value: T) => T)): typeof InstanceDecorator {
return Instance(function(values: Values, name: string) {
let notProcessed = true
let value = undefined
Object.defineProperty(values, name, {
get: function() { return value },
set: function(newValue) {
if (notProcessed) {
value = processor(newValue)
notProcessed = false
} else value = newValue
}
})
})
}
@Instance()
class Values {
@Process<string>(function(value: string): string {
console.log("processed " + value)
return value
})
property: string
}
To get started you will have to install the npm module:
$ npm install instance-decorators
and import it into your script:
const { Instance } = require("instance-decorators")
You have to first decide what you want your decorator to decorate and depending on your decision you will put 2 or 3 arguments for your function.
2 parameters: (target, name), 3 parameters: (target, name, descriptor)
Then write your decorators by either giving it as an argument or using it as a decorator for the method in a class.
// this decorator will only work on properties
const decorator = Instance(function(target, name, descriptor) {
...
})
...
class SomeDecorators {
// this decorator will only work on properties
// (note that we are not calling Instance)
@Instance
decorator(target, name) {
...
}
...
}
...
Decorate the class your decorator will be in with @Instance
then simply use the decorator you created..
@Instance()
class SomeClass {
@decorator
// could also be a property depending on the decorator
method() {
...
}
}
...
After that you can create a new instance for your class like this:
new SomeClass()
Example: Creating an @AutoRun decorator (finished product)
To try this example, you will have to install and import the library first..
We want this decorator to automatically run methods after the class is constructed. This could declutter your code from:
class SomeClass {
constructor() {
this.method()
}
method() {
console.log(`Ran method!`)
}
}
to..
class SomeClass {
@AutoRun()
method() {
console.log(`Ran method!`)
}
}
In this decorator, we want it to accept a list of parameters to give to the function so we will have to create a factory.
// This factory accepts any parameter to pass on to the method
function AutoRun(...parameters: any[]) {
return Instance(function(target: any, name: string, descriptor: PropertyDescriptor) {
})
}
So after that, we will have to add some code to run the function with the passed parameters with method.apply(target, parameters)
function(target: any, name: string, descriptor: PropertyDescriptor) {
// we binded target to the method to ensure that it runs with the class's 'this'
target[name].apply(target, parameters)
}
Then we can use it in any class that has the @Instance()
decorator
@Instance()
class SomeClass {
@AutoRun("some text")
method(someText: string) {
console.log(`Ran method!\nsomeText: '${someText}'`)
// - console.log Output:
// Ran method!
// someText: 'some text'
}
}
// Construct the class
new SomeClass()
const { Instance } = require("instance-decorators")
// This factory accepts any parameter to pass on to the method
function AutoRun(...parameters: any[]) {
return Instance(function(target: any, name: string, descriptor: PropertyDescriptor) {
// we binded target to the method to ensure that it runs with the class's 'this'
target[name].apply(target, parameters)
})
}
// Using it
@Instance()
class SomeClass {
@AutoRun("some text")
method(someText: string) {
console.log(`Ran method!\nsomeText: '${someText}'`)
// - console.log Output:
// Ran method!
// someText: 'some text'
}
}
// Construct the class
// (Try not constructing the class and see what happens..)
new SomeClass()
This returns a decorator that modifies the class in order for instance decorators to work
@Instance()
class SomeClass {
...
}
...
This creates an instance decorator with the given function.
// it has to have 2 or 3 arguments depending on the type of decorator (properties or methods)
const decorator = Instance(function(target, name) { // and optionally a descriptor
...
})
This is mostly the same as the previous one. The only differences are that it replaces the original method with a decorator with a type of typeof InstanceDecorator
and the decorator will be used like @SomeClass.decorator
instead of @decorator
.
class SomeClass {
@Instance
// it has to have 2 or 3 arguments depending on the type of decorator (properties or methods)
static decorator(target, name) { // and optionally a descriptor
...
}
}
The Instance
function and the type InstanceDecorator
are exported and can be acessed like this:
// You need to use 'typeof InstanceDecorator' instead of just 'InstanceDecorator'
const { Instance, InstanceDecorator } = require("instance-decorators")