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

flex-params

v3.2.1

Published

Multiple signatures for a function

Downloads

14

Readme

  • v3.1.0
    • Support simpler syntax
    • Support pipe separated multiple types
  • v3.0.1 [Fix] Broken ES module loading since 3.0.0 has been fixed.
  • v3.0.0 The error handling API changed.

Build Status npm

flex-params is a tiny, but powerful utility for writing a function that has multiple signatures.

As you know, JavaScript doesn't support function overloading like this:

function foo(X)    { /* ... */ }
function foo(X, Y) { /* ... */ } // This makes the 1st foo() not callable

Yes, still you can do the same kind of thing with default parameters most of the time. But if you want more flexibility or scalability, flex-params allows you to define multiple combinations of parameters for a single function.

Getting Started

Install it with NPM:

npm i flex-params

And require() it:

const flexParams = require('flex-params');

or import it as an ES module:

import flexParams from 'flex-params';

Usage

In your function, pass an array of the arguments to flexParams() with any number of patterns of parameters (a.k.a. "signatures") you desired.

// Example Code
function foo(...args) {
  var result = {};

  flexParams(args, [
    { X:'string', Y:'int' },           // pattern #0
    { X:'string', Z:'bool', Y:'int' }, // pattern #1
    { Z:'bool', Y:'int', X:'string' }, // pattern #2
    ...                                // more patterns
  ], result);

  return result;
}

var r = foo('blah', 42);          // { X:'blah',   Y:42 }
var r = foo('blahh', true, 7);    // { X:'blahh',  Y:7,  Z:true }
var r = foo(false, 11, 'blaahh'); // { X:'blaahh', Y:11, Z:false }

In the code above, args is the array of arguments.

The 2nd parameter of flexParams() is an array of the patterns.
flexParams() tries to find the most suitable pattern in the array for args by comparing the type strings defined in each pattern with actual types of args.

Once it is found, each value of args is stored into result ( the 3rd parameter ) as its properties.

If you prefer simpler syntax, you can also write like this:

var result = flexParams(args, [
  ... // patterns
]);

Defining a pattern of parameters

Each pattern must be a plain object that has one of some specific formats.
The most basic format is like this:

// Pattern Definition
{
  param_1st: '<type>',
  param_2nd: '<type>',
  ...
  param_nth: '<type>'
}

'<type>' is a string representation of datatype (ex. 'bool', 'string', 'array', 'object', etc. ) for each param, like this:

{ foo:'string', bar:'boolean' }

This pattern means:

  • The 1st param is foo, and it must be a string
  • The 2nd param is bar, and it must be a boolean

You can also write multiple types separated by | (pipe) likes this:

{ foo:'string|number|boolean' }

This parameter matches with a string, a number or a boolean.

Default Values

Instead of just a type string, you can also use an array to define the default value:

{ foo:'string', bar:['boolean', false] }

Now the 2nd param bar turned to optional. The default value is false.

The pattern that contains optional parameters can be considered suitable even if a fewer number of arguments supplied. And the missing arguments will be filled with the default values of respective params.

How does it work?

// Example Code #1
function foo(...args) {
  let result = {};

  flexParams(args, [
    { flag:['boolean', false] },         // pattern #0
    { str:'string', num:['number', 1] }, // pattern #1
    { num:'number', flag:'boolean' }     // pattern #2
  ], result);

  return result;
}

You can see 3 patterns in the above example.
Let's test it by passing various combinations of arguments:

let test1 = foo();        // No argument
let test2 = foo('ABC');   // A string
let test3 = foo(8, true); // A number, A boolean

console.log('Test 1:', test1);
console.log('Test 2:', test2);
console.log('Test 3:', test3);

And these are the resulting objects:

Test 1: { flag: false }
Test 2: { str: 'ABC', num: 1 }
Test 3: { num: 8, flag: true }

Now you can see:

  • The test1 matched with pattern #0
  • The test2 matched with pattern #1
  • The test3 matched with pattern #2

More Example

// Example Code #2
const flexParams = require('flex-params');

class User {
  constructor(...args) {
    flexParams(args, [
      // patterns
      { firstName:'string', age:'int' },
      { firstName:'string', lastName:'string', age:'int' },
      { id:'int' },
      { login:'string', pass:Password }

    ], this); // Stores the args into 'this'
  }
}

class Password {
  constructor(key) {
    this.key = key;
  }
}

//// Test ////////////
let thomas = new User('Thomas', 20);
let john   = new User('John', 'Doe', 30);
let user1  = new User(1000);
let user2  = new User('d4rk10rd', new Password('asdf'));

console.log(thomas);
console.log(john);
console.log(user1);
console.log(user2);

Console outputs:

User { firstName: 'Thomas', age: 20 }
User { firstName: 'John', lastName: 'Doe', age: 30 }
User { id: 1000 }
User { login: 'd4rk10rd', pass: Password { key: 'asdf' } }

As you can see this example, you can pass this to the 3rd parameter of flexParams(). This way is useful for initializing the instance in the class constructor.

Advanced Usage

About the 3rd parameter of flexParams(), it accepts not only an object but also a function.

function foo(...args) {
  flexParams(args, [
    { flag:['boolean', false] },         // pattern #0
    { str:'string', num:['number', 1] }, // pattern #1
    { num:'number', flag:'boolean' }     // pattern #2

  ], (result, pattern) => { // Receiver Callback
    console.log('result:',  result);
    console.log('pattern:', pattern);
  });
  // Test ////////
  foo('XYZ', 512);
}

Console outputs:

result: { str: 'XYZ', num: 512 }
pattern: 1

Let's call this function a receiver callback. Receiver callback runs immediately after flexParams() finished processing args.

Receiver callback takes 2 parameters: result and pattern.
result is an object that contains all the args as its properties. pattern is the index number of the matched pattern, which is 1 ( means pattern #1 ) at this time.
This index is useful if you want to do some different things for each pattern with switch-case or if-else-if, like this:

function foo(...args) {
  return flexParams(args, [
    { flag:['boolean', false] },         // pattern #0
    { str:'string', num:['number', 1] }, // pattern #1
    { num:'number', flag:'boolean' }     // pattern #2

  ], (result, pattern) => { // Receiver Callback
    switch (pattern) { // Do stuff for each pattern
      case 0: return 'The first pattern matched.';
      case 1: return 'The second pattern matched.';
      case 2: return 'The last pattern matched.';
    }
  });
}
//// Test ////////
console.log( foo('XYZ', 512)   ); // 'The second pattern matched.'
console.log( foo(65535, false) ); // 'The last pattern matched.'
console.log( foo()             ); // 'The first pattern matched.'

Error Handling

If all the given patterns didn't match for the arguments, flexParams() returns false. But there is also another way to handle this situation. The 4th parameter: fallback.

fallback can be:

  • a) a callback,
  • b) an object, or
  • c) any other type of value ( except for undefined ).

a) If you passed a callback, it is called when all the given patterns mismatched.
Tha callback receives an object as its parameter with these properties:

  • args: The original array of arguments
  • patterns: The array of all the patterns
  • receiver: The receiver object or function passed to the 3rd parameter
  • error: An Exception object ( flexParams.InvalidArgument )

b) You can pass an plain object as the fallback with these optional properties:

  • log: A string to be sent to console.log()
  • warn: A string to be sent to console.warn()
  • error: A string to be sent to console.error()
  • throw: Any type of value to be thrown as an exception
    • If you set true , an Exception object is thrown ( flexParams.InvalidArgument )

c) Any other type of value as the fallback , is returned straightly by flexParams() if all the given patterns mismatched.

Appendix: Extra Types

flex-params supports some special types in addition to JavaScript's builtin datatypes.

| Type | Description | |-----:|:------------| | bool | Alias of boolean | | int, integer | Matches for an integer | | float, double | Alias of number | | array | Matches for an array | | iterable | Matches for an array or an array-like object | | primitive | Matches for a primitive type value | | any, mixed | Matches for any type of value | | A class constructor | Matches for the class instances |


© 2020 amekusa