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

stronganator

v2.0.2

Published

Types and run time type checking for JavaScript

Downloads

11

Readme

stronganator

Types with run time type checking for JavaScript

Why

To keep some sanity in large projects, and to provide detailed debugging information to developers.

Usage

Stronganator exports several interfaces, types, type, func, model and match;

Types

import { T } from 'stronganator'

T is an object that has several properties hanging off of it. These properties are functions that take a single parameter and return a Boolean. These functions check the provided parameter to see if it is of a certain type, and return the result. An example of the Number type is

console.log(T.Number(1)); // true
console.log(T.Number('1')); // false

A list of the provided types is below.

  • Any: always returns true,
  • Truthy: returns true if the item is truthy,
  • Falsey: returns false if the item is falsey,
  • Date: returns true if the item is of new Date,
  • Nil: returns true if the item is null or undefined,
  • String: returns true if the item is a string,
  • Number: returns true if the item is a number,
  • Boolean: returns true if the item is a boolean,
  • Function: returns true if the item is a function,
  • Error returns true if the item is an instanceof Error,
  • RegExp: returns true if the item is an instanceof RegExp
  • Hash: returns true if the item is an object.

All the above functions can be called in the the following manner.

if(T.Date(item)) {...}
...
items.filter(T.String) //filters out all non strings

Custom types

To make your own types with stronganator you can use the type factory function. It is a higher order function which takes three parameters.

type(name: string, checker: function, types: object || array)

  • name is the name of the type, such as Boolean or User.
  • checker is the function that is responsible for checking if the item prescribes the the desired type. For example, the checker for the Nil type is (nil) => nil === null || nil === undefined.
  • types used for detailed error reporting when creating generics.
Example
Non-generic
const Nil = type('Nil', (nil) => nil === null || nil === undefined); //non-generic
Generic
const Array = (internalType) => {
  return type('Array', (arr) => arr.every(internalType), internalType);
};

const StringArray = T.Array(T.String);

Generics

Stronganator has the power to model generic types, this is done through the use of higher order functions. An example of a generic type is an Array as it simply a container for other values. To use the Array type (and any generic type) you must first provide a type definition. Then, a function is returned that validates that type definition against the provided data.

The provided generic types are

  • Array: ƒ(types: [Types]) -> ƒ(items: Type) -> Boolean
  • Spread: f([types: [Types]]) -> f(items: Type) -> Boolean
  • Object: ƒ(types: Object({ typeName: Type })) -> ƒ(item: Object) -> Boolean
  • Union: ƒ(types: [Types]) -> ƒ(items: Any) -> Boolean
  • Tuple: ƒ(types: [Type1, Type2 ...]) -> ƒ(tuple: [type1, type2 ...]) -> Boolean
  • Optional: ƒ(type: Type) -> ƒ(x: Type || Nil) -> Boolean
Usage
Object

The Object generic consumes an object that defines the types of it's properties. It then returns a function who is passed an object, and validates the properties of that function against the original provided T.

const User = T.Object({
	name: T.String
});
// returns a function that checks that the provided item is of type { name: type.String }.

console.log(User({ name: 'gwash' })); // true
console.log(User({ name: 1 })); // false

You can also nest Object types such as

const DetailedUser = T.Object({
  name: T.String,
  details: T.Object({
    phoneNumber: T.Number,
    address: T.String
  });
})

console.log(DetailedUser({
  name: 'gwash',
  details: {
    phoneNumber: 555,
    address: 'no where'
  }
})); //true

console.log(DetailedUser({
  name: 'gwash',
  details: {
    phoneNumber: 555,
    address: 123
  }
})); //false
Array
const UserArray = T.Array(userType);
// returns a function that checks that all elements pass the provided type checking
console.log(UserArray([
    { name: 'gwash' }
]); // true

console.log(UserArray([
  {name: 1}
]); // false
Spread
const UserSpread = T.Spread(userType);
// returns a function that checks that all elements pass the provided type checking
const firstUser = func(UserSpread)
  .of((...users) => user[0]);
Union
const Student = type('Student', (x) => T.Number(x.id));

const StudentUserUnion = T.Union([Student, User]);

console.log(StudentUserUnion({ name: 'gwash' })); //true
console.log(StudentUserUnion({ id: 1})); //true
console.log(StudentUserUnion({ name: 1 })); //false
console.log(StudentUserUnion({ id: '1' })); //false
Tuple
const Student = T.Tuple([T.String, T.Number]);
//position 1 must be a string
//position 2 must be a number

console.log(Student(['gwash', 1234])) //true

console.log(Student([1234, 'gwash'])) //false
Optional
const OptionalNumber = T.Optional(T.Number);
console.log(OptionalNumber(4)) //true
console.log(OptionalNumber('')) //false
console.log(OptionalNumber()) //true
Type Extension

Types can be extended. This creates a strict union between the parent type, and the child type.

Example
const ActionType = T.String.extend('Action', (s) => s.indexOf('/') > 0);
const RandomActionType = ActionType.extend('RandomAction', s => s.indexOf('random') > 0);

console.log(ActionType('/')) //true
console.log(RandomAction('/random')) //true
console.log(RandomAction('random')) //false

Functions

import { func } from 'stronganator';

Stronganator also does typed functions. This is done through the func higher order function. The function signature for func is

func(type: [Type] || Type, returnType: Any?) -> { of: (ƒ -> returnType) } -> ƒ

  • types is an array who elements match up to the provided parameters. For example, the following call, func([type.Date]), would expect that the first parameter to is a Date.
  • returnType: is simply a Type (created with the type function) that ensures the type of returnType.

Example

const getName = func([User], T.String).of((user) => user.name);
console.log(getName({name: 'gwash'})); // 'gwash'
console.log(getName({name: 1})); // TypeError: Function returned a number but needed a String
const getName = func([userType], T.String).of((user) => 1);
console.log(getName({name: 'gwash'})); // TypeError: Needed [{ "name": "String"}] but got [{"name":1}]

Pattern Matching

import { match } from 'stronganator';

match: ƒ(tuples: [Type, Function], ...) -> ƒ(items: Type) -> Any

Pattern matching works by accepting multiple Tuples. These Tuples are of [type.Type, type.Function].

The match function returns the pattern matching function. This pattern matching function accepts only the types that are to be matched upon

Example

const Match = match(
  [T.String, (str) => console.log('String:', str)],
  [T.Number, (n) => console.log('Number:', n)]
);

console.log(Match(5)) //Number: 5
console.log(Match('5')) //String: 5

console.log(Match({})) // TypeError: Needed ["Number || Even"] but got [{}]

Additionally, if there are multiple matches for a provided argument, a TypeError will be thrown.

const Match = match(
  [T.Number, (n) => n * 1
  [T.Number, (n) => n * 2
);

console.log(Match('5')) //TypeError: 5 matched more than one type. Only one type must be matched. Type: Number, Result: 5 Type: Number, Result: 10

Lastly, if you want to have a fall through case, you can use the Default type.

const Match = match(
  [T.String, (str) => console.log('String:', str)],
  [T.Number, (n) => console.log('Number:', n)],
  [T.Default, (x) => console.log('Item not supported:', x)]
);

Model

import { model } from 'stronganator'

Used to describe a class, model or objects types. The resulting type only checks the for defined properties. It will not type check undefined properties;

object: ƒ(object: {property: PropertyType}) -> ƒ(object: PropertyType) -> PropertyType

Note Use function keyword to make typed functions here, so they get bound to the object instance

Example

const UserType = model({
  name: T.String,
  setName: T.Function,
  getName: T.Function
});
let user;

user = UserType({
  name: 'Adam',
  setName: func(T.String)
           .of(funtion(name) {
             this.name = name;
           }),
  getName: func([], T.String)
           .of(function() {
             return this.name;
           }),
}); // returns instance

user = UserType({
  name: 'Adam',
  setName: func(T.String)
           .of(funtion(name) {
             this.name = name;
           }),
  getName: func([], T.String)
           .of(function() {
             return this.name;
           }),
  birthday: new Date() //untyped and unchecked
});

user = UserType({
  name: 'Adam',
  setName: func(T.String)
           .of(funtion(name) {
             this.name = name;
           })
}); //TypeError

If you would prefer to use the ES6 function syntax () => {} then the object instance is available as the last parameter.

const UserType = model({
  name: T.String,
  setName: T.Function,
  getName: T.Function
});
let user;

user = UserType({
  name: 'Adam',
  setName: func(T.String)
           .of((name, user) => {
             user.name = name;
           }),
  getName: func([], T.String)
           .of((user) => {
             return user.name;
           }),
}); // returns instance

Extension

Model types can be extended by using the .extend method.

Example
const StudentType = UserType.extend({
  id: T.String
});
...

const student = StudentType({
  name: 'Adam',
  getName() {},
  setName() {},
  id: '123'
}); //returns instance

const student = StudentType({
  name: 'Adam',
  getName() {},
  setName() {},
  id: 123
}); //TypeError

More information

Head to the details page