instance-decorators
TypeScript icon, indicating that this package has built-in type declarations

1.1.4 • Public • Published

Instance Decorators (npm | git)

Note: the git version has the lastest corrections to this file.

This npm library enables users to create decorators that run when the class constructs.

$ npm install instance-decorators

Pages (or skip)

Some use cases

There are a huge range of applicable reasons and places where using this library benifits you (and your library/frameworks's users) including..

1. Using it for providing values to the user

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
}

2. Using it for making 'this' readonly

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() {
        ...
    }
}

3. Using it for processing values that were set the first time

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
}

Guide

Step 1

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")

Step 2

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.

Choice 1: giving it as an argument

// this decorator will only work on methods
const decorator = Instance(function(target, name, descriptor) {
    ...
})
...

Choice 2: adding the decorator onto the decorator

class SomeDecorators {
    // this decorator will only work on properties
    // (note that we are not calling Instance)
    @Instance
    decorator(target, name) {
        ...
    }
    ...
}
...

Step 3

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() {
        ...
    }
}
...

Step 4

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..

What we want it to do

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..

@Instance()
class SomeClass {
    @AutoRun()
    method() {
        console.log(`Ran method!`)
    }
}

Creating the decorator factory

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) {
        ...
    })
}

Adding code for the decorator

So after that, we will have to add some code to run the function with the passed parameters with method.apply(target, parameters)

// for the function passed to 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 the decorator

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()

Finished product

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()

Documentation

@Instance(): ((target: any) => any)

This returns a decorator that modifies the class in order for instance decorators to work

Example

@Instance()
class SomeClass {
    ...
}
...

Instance(action: InstanceAction): InstanceDecorator

This creates an instance decorator with the given function.

Example

// 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
    ...
})

@Instance

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.

Example

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
        ...
    }
}

Imports

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")

Package Sidebar

Install

npm i instance-decorators

Weekly Downloads

2

Version

1.1.4

License

MIT

Unpacked Size

14.6 kB

Total Files

6

Last publish

Collaborators

  • charlesyiu