npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2024 – Pkg Stats / Ryan Hefner

@final-hill/class-tools

v1.2.3

Published

Class Tools provides a number of utility functions and decorators to ease the use of features commonly found in functional languages

Downloads

21

Readme

Class Tools

Build npm version Downloads

Table of Contents

Introduction

This library provides a number of utility decorators to enable the use of features commonly found in functional languages for use with classes.

Note that the license for this library is AGPL-3.0-only. You should know what that means before using this library. If you would like an exception to this license per section 7 contact the author.

Library Installation

As a dependency run the command:

npm install @final-hill/class-tools

You can also use a specific version:

npm install @final-hill/[email protected]

For use in a webpage:

<script src="https://unpkg.com/@final-hill/class-tools"></script>

With a specific version:

<script src="https://unpkg.com/@final-hill/[email protected]></script>

Memoization

The @memo decorator memoizes (caches) the results of the associated method call.

import {memo} from '@final-hill/class-tools';

class Fib {
    @memo
    calcMemo(n: number): number {
        return n < 2 ? n : this.calcMemo(n - 1) + this.calcMemo(n - 2);
    }
    calc(n: number): number {
        return n < 2 ? n : this.calc(n - 1) + this.calc(n - 2);
    }
}

fib.calc(30) // 832040; 9ms
fib.calcMemo(30) // 832040; less than 1ms

Currying

The @curry decorator converts the associated method into a method that supports currying the parameters.

import {curry} from '@final-hill/class-tools';

class Adder {
    @curry
    add(a: number, b: number): number {
        return a + b;
    }
}

const adder = new Adder(),
      addOne = adder.add(1);

addOne(3) // 4
addOne()(3) // 4

Partial Application

The @partial decorator converts the associated method into one that supports partial application of its parameters

import {partial, _} from '@final-hill/class-tools';

class A {
    @partial
    m(a: number, b: number, c: number): number { return a + b + c; }
}

const a = new A();

a.m(1,2,3) === 6
a.m(_,2,3)(1) === 6
a.m(1,_,3)(2) === 6
a.m(1,2,_)(3) === 6
a.m(1,_,_)(2,3) === 6
a.m(_,2,_)(1,3) === 6
a.m(_,_,3)(1,2) === 6
a.m(_,_,_)(1,2,3) === 6
a.m(_,_,_)(_,2,_)(1,3) === 6

Lazy Fields

The @lazy decorator converts the associated getter into a lazily initialized field. Practically this means that the body of the getter will only executed once on its first use. Subsequent usages will return the cached result of the first call.

import {lazy} from '@final-hill/class-tools';

class Counter {
    static usage = 0;
    constructor(){
        Counter.usage++;
    }
}

class Foo {
    @lazy
    get bar(): Counter { return new Counter(); }
}

const foo = new Foo();

void foo.bar;
void foo.bar;
void foo.bar;

Counter.usage // 1

Fixed-Point

The @fix decorator can be assigned to methods in order to control the behavior of its recursion and find its least fixed-point. It provides options to limit runaway recursion as well as handle self-referential calls while returning a value.

The bottom option defines the value to return when the recursive call bottoms out. In other words, if the current method has been recursively called with the same arguments then it is replaced with the value given.

import {fix} from '@final-hill/class-tools';

class Foo {
    @fix({bottom: 0})
    bar(): number {
        return this.bar();
    }
}

new Foo().bar() === 0;

Recursive calls may always vary in their arguments leading to runaway recursion in a different way. The limit option prevents infinite recursion by replacing the nth call with the value defined by the bottom option:

import {fix} from '@final-hill/class-tools';

class Foo {
    @fix({bottom: 0, limit: 10})
    bar(n: number): number {
        return 1 + this.bar(n + 1);
    }
}

new Foo().bar(0) === 10;

The bottom option can also be defined as a function if a computed result is desired:

import {fix} from '@final-hill/class-tools';

class Foo {
    @fix({bottom: (n: number) => n**2})
    bar(n: number): number {
        if(n <= 3) {
            return 1 + this.bar(n + 1);
        } else {
            return this.bar(n); // bottom(4) == 4**2 == 16
        }
    }
}
new Foo().bar(0) === 20;

Known Limitations

When using TypeScript a decorator can not change the type of the associated class feature. This is a limitation of the language. Depending on your usage you may need to perform explicit casting or utilize the // @ts-ignore option. For example:

import {partial, _} from '@final-hill/class-tools';

class A {
    @partial
    m(a: number, b: number, c: number): number { return a + b + c; }
}

const a = new A();

a.m(1,2,3) === 6
// @ts-ignore
a.m(_,2,3)(1) === 6
// @ts-ignore
a.m(1,_,3)(2) === 6
// @ts-ignore
a.m(1,2,_)(3) === 6

Further Reading