Skip to content

Commit

Permalink
Added more types, added tests and upgraded libraries
Browse files Browse the repository at this point in the history
  • Loading branch information
giorgosart committed Sep 7, 2020
1 parent b6dba84 commit bfe500b
Show file tree
Hide file tree
Showing 7 changed files with 239 additions and 48 deletions.
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -2,3 +2,4 @@ node_modules
.idea
dist
package-lock.json
coverage
9 changes: 9 additions & 0 deletions .npmignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
src/
.babelrc
.npmignore
LICENSE
.github
.idea
coverage
README.md
webpack.config.js
8 changes: 4 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
[![Test](https://img.shields.io/npm/v/strong-typed.svg?style=flat)](https://www.npmjs.com/package/strong-typed)
[![install size](https://packagephobia.now.sh/badge?p=strong-typed@latest)](https://packagephobia.now.sh/result?p=strong-typed@latest)
[![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)


Expand All @@ -15,16 +16,15 @@ Strong-typed takes two arguments:

| Parameter | Type | Description |
|-----------|----------|-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| Types | array | A javascript array of supported types (supported types are `any`, `date`, `integer`, `boolean`, `decimal`, `string`, `object`, `function` and `array`). The number of elements in the array **must** match the parameter number of function that's passed in. |
| Types | array | A javascript array of supported types (supported types are `any`, `date`, `integer`, `boolean`, `decimal`, `string`, `object`, `function`, `array`, `set` and `map`). The number of elements in the array **must** match the parameter number of function that's passed in. |
| Fn | function | The javascript function to be type checked |
### Examples
```
import st from 'strong-typed';
import strongTyped, {Types} 'strong-typed';
const myFunc = st(['string'], (a) => { console.log(a) });
const myFunc = st([Types.STRING], (a) => { console.log(a) });
myFunc(); // Error: Function expects 1 arguments, instead only 0 parameter(s) passed in.
myFunc(1); // Error: Expected argument 1 to be of type 'string'
myFunc("1"); // Will print 1 in the console
```

16 changes: 8 additions & 8 deletions package.json
Original file line number Diff line number Diff line change
@@ -1,13 +1,13 @@
{
"name": "strong-typed",
"version": "0.2.0",
"version": "0.3.0",
"description": "A runtime type-checking library for javascript",
"main": "src/index.js",
"scripts": {
"dev": "webpack --mode development",
"build": "webpack --mode production",
"prepublish": "npm run build",
"test": "jest"
"test": "jest --coverage"
},
"keywords": [
"static-types",
Expand All @@ -27,11 +27,11 @@
},
"homepage": "https://github.com/giorgosart/strong-typed#readme",
"devDependencies": {
"@babel/core": "^7.4.3",
"@babel/preset-env": "^7.4.3",
"babel-loader": "^8.0.5",
"jest": "^24.7.1",
"webpack": "^4.30.0",
"webpack-cli": "^3.3.1"
"@babel/core": "^7.11.6",
"@babel/preset-env": "^7.11.5",
"babel-loader": "^8.1.0",
"jest": "^26.4.2",
"webpack": "^4.44.1",
"webpack-cli": "^3.3.12"
}
}
36 changes: 36 additions & 0 deletions src/errors.test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
import strongTyped, {Types} from "./index";

const fn = (val) => strongTyped([Types.STRING], (a) => { console.log(a) });
const fn_falsy = (val) => strongTyped(['asdf'], (a) => { console.log(a) });
const testFn = (a) => { console.log(a) };

test('valid string input', () => {
expect(fn("string")).toBeTruthy();
});

test('invalid type declared', () => {
expect(fn_falsy(1)).toBeTruthy();
});

test("parameter number validation error message", () => {
const myFunc = strongTyped([Types.STRING], testFn);
expect(() => {myFunc()}).toThrow(Error);
expect(() => {myFunc()}).toThrow("Function testFn expects 1 arguments, instead only 0 parameter(s) passed in.");
});

test("extra parameter types number validation error thrown", () => {
expect(() => {strongTyped([Types.STRING, Types.STRING], testFn)}).toThrow(Error);
expect(() => {strongTyped([Types.STRING, Types.STRING], testFn)}).toThrow("Function testFn has 1 argument(s) and only 2 type(s) defined.");
});

test("parameter types number validation error message", () => {
const myFunc = strongTyped([Types.STRING], testFn);
expect(() => {strongTyped([], testFn)}).toThrow(Error);
expect(() => {myFunc()}).toThrow("Function testFn expects 1 arguments, instead only 0 parameter(s) passed in.");
});

test("unsupported parameter types validation error message", () => {
const myFunc = strongTyped(["Blah"], testFn);
expect(() => {myFunc("test")}).toThrow(Error);
expect(() => {myFunc("test")}).toThrow("Unsupported type found in argument 1 for function testFn, supported types are any,date,integer,bigint,boolean,decimal,string,object,function,array,set,map");
});
90 changes: 54 additions & 36 deletions src/index.js
Original file line number Diff line number Diff line change
@@ -1,83 +1,101 @@
const strongTyped = (types, fn) => {

const ANY = 'any';
const DATE = 'date';
const INTEGER = 'integer';
const BOOLEAN = 'boolean';
const DECIMAL = 'decimal';
const STRING = 'string';
const OBJECT = 'object';
const FN = 'function';
const ARRAY = 'array';

const staticTypes = [ANY, DATE, INTEGER, BOOLEAN, DECIMAL, STRING, OBJECT, FN,
ARRAY];

if (types.length !== fn.length) {
throw new Error(`Function has ${fn.length} argument(s) and only ${types.length} type(s) defined`);
throw new Error(`Function ${fn.name} has ${fn.length} argument(s) and only ${types.length} type(s) defined.`);
}

let isValidType = (value, type, argIndex) => {
const isValidType = (value, type, argIndex) => {
switch (type.toLowerCase()) {
case ANY :
return true;
case DATE :
if (typeof value === OBJECT && value instanceof DATE) {
case Types.ANY :
break;
case Types.DATE :
if (typeof value === Types.OBJECT && value instanceof Types.DATE) {
return true;
}
break;
case BOOLEAN :
if (typeof value === BOOLEAN) {
case Types.BOOLEAN :
if (typeof value === Types.BOOLEAN) {
return true;
}
break;
case STRING :
if (typeof value === STRING) {
case Types.STRING :
if (typeof value === Types.STRING) {
return true;
}
break;
case INTEGER :
case Types.BIG_INT:
if (typeof value === Types.BIG_INT) {
return true;
}
break;
case Types.INTEGER :
if (typeof value === 'number' && Number.isInteger(value)) {
return true;
}
break;
case DECIMAL :
if (typeof value === 'number' && !Number.isInteger(value)) {
case Types.DECIMAL :
if (typeof value === 'number' && !Number.isInteger(value) && !isNaN(value)) {
return true;
}
break;
case Types.OBJECT :
if (typeof value === Types.OBJECT) {
return true;
}
break;
case OBJECT :
if (typeof value === OBJECT) {
case Types.FN :
if (typeof value === Types.FN) {
return true;
}
break;
case FN :
if (typeof value === FN) {
case Types.ARRAY :
if (typeof value === Types.OBJECT && Array.isArray(value)) {
return true;
}
break;
case ARRAY :
if (typeof value === OBJECT && Array.isArray(value)) {
case Types.SET :
if (typeof value === "object" && value instanceof Set) {
return true;
}
break;
case Types.MAP :
if (typeof value === "object" && value instanceof Map) {
return true;
}
break;
default :
throw new Error(`Unsupported type found in argument ${argIndex + 1}, supported types are ${staticTypes.toString()}`);
throw new Error(`Unsupported type found in argument ${argIndex + 1} for function ${fn.name}, supported types are ${Object.values(Types)}`);
}
throw new Error(`Expected argument ${argIndex + 1} to be of type '${type}'`);
throw new Error(`Function ${fn.name} expected argument ${argIndex + 1} to be of type '${type}'`);
};

return (...args) => {
if (types.length !== args.length) {
throw new Error(`Function expects ${fn.length} arguments, instead only ${args.length} parameter(s) passed in.`);
throw new Error(`Function ${fn.name} expects ${fn.length} arguments, instead only ${args.length} parameter(s) passed in.`);
}

for (let i = 0; i < args.length; i++) {
isValidType(args[i], types[i], i)
isValidType(args[i], types[i], i);
}

return fn(...args);
}
};

export const Types = {
ANY: 'any',
DATE: 'date',
INTEGER: 'integer',
BIG_INT: 'bigint',
BOOLEAN: 'boolean',
DECIMAL: 'decimal',
STRING: 'string',
OBJECT: 'object',
FN: 'function',
ARRAY: 'array',
SET: 'set',
MAP: 'map'
};
Object.freeze(Types);

export default strongTyped;
127 changes: 127 additions & 0 deletions src/types.test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,127 @@
import strongTyped, {Types} from "./index";

const testFn = (a) => { console.log(a) };

test("Any input", () => {
const myFunc = strongTyped([Types.ANY], testFn);
expect(() => {myFunc("1")}).toBeTruthy();
expect(() => {myFunc(1)}).toBeTruthy();
expect(() => {myFunc(1.0)}).toBeTruthy();
expect(() => {myFunc([])}).toBeTruthy();
expect(() => {myFunc({})}).toBeTruthy();
expect(() => {myFunc(new Set())}).toBeTruthy();
expect(() => {myFunc(new Date())}).toBeTruthy();
});

test("String input", () => {
const myFunc = strongTyped([Types.STRING], testFn);
expect(() => {myFunc("1")}).toBeTruthy();

expect(() => {myFunc(1)}).toThrow(Error);
expect(() => {myFunc(1)}).toThrow("Function testFn expected argument 1 to be of type 'string'");
});

test("Big integer input", () => {
const myFunc = strongTyped([Types.BIG_INT], testFn);
//expect(() => {myFunc(9007199254740991n)}).toBeTruthy();
expect(() => {myFunc(BigInt(9007199254740991))}).toBeTruthy();
expect(() => {myFunc(BigInt("0x1fffffffffffff"))}).toBeTruthy();
expect(() => {myFunc(BigInt("0b11111111111111111111111111111111111111111111111111111"))}).toBeTruthy();

expect(() => {myFunc("test")}).toThrow(Error);
expect(() => {myFunc("test")}).toThrow("Function testFn expected argument 1 to be of type 'bigint'");
});

test("Array input", () => {
const myFunc = strongTyped([Types.ARRAY], testFn);
expect(() => {myFunc(['Apple', 'Banana'])}).toBeTruthy();
expect(() => {myFunc([1, 2])}).toBeTruthy();
expect(() => {myFunc([[1], [2]])}).toBeTruthy();


expect(() => {myFunc(new Date())}).toThrow(Error);
expect(() => {myFunc("test")}).toThrow(Error);
expect(() => {myFunc("test")}).toThrow("Function testFn expected argument 1 to be of type 'array'");
});

test("Boolean input", () => {
const myFunc = strongTyped([Types.BOOLEAN], testFn);
expect(() => {myFunc(true)}).toBeTruthy();
expect(() => {myFunc(false)}).toBeTruthy();
expect(() => {myFunc(0)}).toBeTruthy();
expect(() => {myFunc(1)}).toBeTruthy();

expect(() => {myFunc("test")}).toThrow(Error);
expect(() => {myFunc("test")}).toThrow("Function testFn expected argument 1 to be of type 'boolean'");
});

test("Date input", () => {
const myFunc = strongTyped([Types.DATE], testFn);
expect(() => {myFunc(Date.now())}).toBeTruthy();
expect(() => {myFunc(Date.UTC())}).toBeTruthy();
expect(() => {myFunc(new Date())}).toBeTruthy();
expect(() => {myFunc(new Date('December 17, 1995 03:24:00'))}).toBeTruthy();
expect(() => {myFunc(new Date(new Date('1995-12-17T03:24:00')))}).toBeTruthy();

expect(() => {myFunc({})}).toThrow(Error);
expect(() => {myFunc("test")}).toThrow(Error);
expect(() => {myFunc("test")}).toThrow("Function testFn expected argument 1 to be of type 'date'");
});

test("Decimal input", () => {
const myFunc = strongTyped([Types.DECIMAL], testFn);
expect(() => {myFunc(2.333)}).toBeTruthy();
expect(() => {myFunc(Number.EPSILON)}).toBeTruthy();

expect(() => {myFunc(1)}).toThrow(Error);
expect(() => {myFunc(Number.NaN)}).toThrow(Error);
expect(() => {myFunc("test")}).toThrow(Error);
expect(() => {myFunc("test")}).toThrow("Function testFn expected argument 1 to be of type 'decimal'");
});

test("Integer input", () => {
const myFunc = strongTyped([Types.INTEGER], testFn);
expect(() => {myFunc(123)}).toBeTruthy();
expect(() => {myFunc(Number.MAX_SAFE_INTEGER)}).toBeTruthy();

expect(() => {myFunc(Number.EPSILON)}).toThrow(Error);
expect(() => {myFunc(Number.NaN)}).toThrow(Error);
expect(() => {myFunc("test")}).toThrow(Error);
expect(() => {myFunc("test")}).toThrow("Function testFn expected argument 1 to be of type 'integer'");
});

test("Function input", () => {
const myFunc = strongTyped([Types.FN], testFn);
expect(() => {myFunc(testFn("test"))}).toBeTruthy();

expect(() => {myFunc("test")}).toThrow(Error);
expect(() => {myFunc("test")}).toThrow("Function testFn expected argument 1 to be of type 'function'");
});

test("Object input", () => {
const myFunc = strongTyped([Types.OBJECT], testFn);
expect(() => {myFunc(testFn({}))}).toBeTruthy();

expect(() => {myFunc("test")}).toThrow(Error);
expect(() => {myFunc("test")}).toThrow("Function testFn expected argument 1 to be of type 'object'");
});

test("Set input", () => {
const myFunc = strongTyped([Types.SET], testFn);
expect(() => {myFunc(testFn(new Set()))}).toBeTruthy();
expect(() => {myFunc(testFn(new Set([1, 2, 3, 4])))}).toBeTruthy();

expect(() => {myFunc({})}).toThrow(Error);
expect(() => {myFunc("test")}).toThrow(Error);
expect(() => {myFunc("test")}).toThrow("Function testFn expected argument 1 to be of type 'set'");
});

test("Map input", () => {
const myFunc = strongTyped([Types.MAP], testFn);
expect(() => {myFunc(testFn(new Map()))}).toBeTruthy();

expect(() => {myFunc({})}).toThrow(Error);
expect(() => {myFunc("test")}).toThrow(Error);
expect(() => {myFunc("test")}).toThrow("Function testFn expected argument 1 to be of type 'map'");
});

0 comments on commit bfe500b

Please sign in to comment.