repropose

1.0.2 • Public • Published

🎲 repropose

Build Status Coverage Status code style: prettier All Contributors PRs Welcome

A set of higher order functions that will, based on the given base function, create a new function decorated with additional function and instance properties ("props").

Install

npm install --save repropose

Or if you prefer yarn:

yarn add repropose

Usage

The library exposes two higher order functions: withProps and withStaticProps. Use them to create a new function decorated with additional function and instance properties ("props").

In the following examples, to make the code more readable, we utilize the compose function from the popular ramda library (lodash also offers this).

import { compose } from "ramda";
import { withProps, withStaticProps } from "repropose";
 
const Vehicle = compose(
  withProps(() => ({
    size: "",
    color: "",
    weight: 0
  })),
  withStaticProps({
    type: "vehicle"
  })
)(function() {});
 
console.log(Vehicle.type); // "vehicle"
 
const vehicle = new Vehicle();
vehicle.size = "large";
vehicle.color = "red";
 
console.log(vehicle.size); // "large"
console.log(vehicle.color); // "red"

Both withProps and withStaticProps can accept an object that represents new to-be-assigned props or a function that returns it. The benefit of using a function is that it receives a reference to the new instance (withProps) and new function (withStaticProps) as first argument, which is useful when there's a need to check already assigned props on the previous / base function.

Note that functions created with shown HOFs are completely new functions, and are not linked in any way with the base functions.

Composing functions

It's also possible to compose existing functions with a new set of props. Although this was basically already done in the previous example (base function was an empty function), a more practical example would be composing the existing Vehicle function with a new set of props, thus creating a new Car function:

// All instances of Car function will be comprised of all "Vehicle" 
// function's props and "doorsCount", "seatsCount" and "speed" props.
const Car = compose(
  withProps({
    doorsCount: 0,
    seatsCount: 0,
    speed: 0
  })
)(Vehicle);
 
console.log(Vehicle.type); // "vehicle"
console.log(Car.type); // "car"
 
const car = new Car();
car.size = "large";
car.color = "red";
car.seatsCount = 5;
 
console.log(car.size); // "large"
console.log(car.color); // "red"
console.log(car.seatsCount); // 5

From here we can go even further, and define a few additional functions that could be comprised of all Car functions' properties and additional specific-car-type ones. Compose as many functions as needed.

Functions as new props

Props can also be functions:

const Car = compose(
  withProps({
    dorsOpenedCount: 0,
    openDoors(count) {
      this.dorsOpenedCount = count;
    }
    hasOpenedDoors() {
      return this.dorsOpenedCount > 0;
    }
  })
)(Vehicle);
 
const car = new Car();
console.log(car.hasOpenedDoors()); // false
 
car.openDoors(2);
 
console.log(car.dorsOpenedCount); // 2
console.log(car.hasOpenedDoors()); // true

Note: don't use arrow functions if the function is working with this like in the above example, since it won't hold the correct object reference.

Custom HOFs

You can also create custom HOFs which you can selectively apply where needed. Consider the following example:

// We extracted the Car props into a custom HOF.
const withCarProps = () => {
  return fn => {
    return compose(
      withProps({
        doorsCount: 0,
        seatsCount: 0,
        speed: 0,
        getSpeed() {
          return this.speed;
       }
      })
    )(fn)
  }
};
 
// Where needed, apply "withNitro" HOF to append a piece of 
// functionality to an existing function and its instances.
const withNitro = ({ nitroSpeedMultiplier }) => {
  return fn => {
    return compose(
      withProps({
        nitroEnabled: false,
        enableNitro() {
          this.nitroEnabled = true;
        },
        getSpeed() {
          if (this.nitroEnabled) {
            return this.speed * nitroSpeedMultiplier;
          }
          return this.speed;
        }
      })
    )(fn)
  }
};
 
const Car = compose(
  withNitro({ nitroSpeedMultiplier: 2.5 }),
  withCarProps()
)(Vehicle);
 
const car = new Car();
car.speed = 100;
 
console.log(car.getSpeed()); // 100
 
car.enableNitro();
 
console.log(car.nitroEnabled); // true
console.log(car.getSpeed()); // 250

You can use this idea to create a set of HOFs that are responsible for applying specific functionality and then compose them as necessary.

The motivation

This library is based on the composition over inheritance premise.

The main problem with the inheritance approach is that it forces you to define your classes / entities early in the project, which will, as new requests arrive, almost certainly result in structural inneficiencies / refactors in the future. This video describes the pros and cons of both approaches very well, so I encourage you to check it out (thanks MPJ).

🍌🦍🌴🙃

Reference

withProps(props: {[string]: any} | (newInstance : Object) => {[string]: any}): Function

Creates a new function, whose instances are decorated with given props.

withStaticProps(props: {[string]: any} | (newFunction : Function) => {[string]: any}): Function

Creates a new function, with props assigned directly to it.

Contributors

Thanks goes to these wonderful people (emoji key):


Adrian Smijulj

💻 📖 💡 👀 ⚠️

This project follows the all-contributors specification. Contributions of any kind welcome!

Package Sidebar

Install

npm i repropose

Weekly Downloads

650

Version

1.0.2

License

MIT

Unpacked Size

46.4 kB

Total Files

36

Last publish

Collaborators

  • adrian1210