Created
January 20, 2016 21:56
-
-
Save ctrlplusb/cdffa1fc1b14f427b790 to your computer and use it in GitHub Desktop.
Using tcomb to type check your functions without annotations.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import { isType } from 'tcomb'; | |
/** | |
* (...values) => (...types) => value||[values] | |
* | |
* This is a curried function to help with type checking your javascript. | |
* It is curried in two parts, the first part taking the values that you | |
* wish to type check, the second part of the curry takes the types you wish | |
* to validate them with. | |
* | |
* If your types are valid then your values are returned. In the case of there | |
* being multiple values the values shall be returned within an Array. In the | |
* case of there being a single value the single value shall be returned, this | |
* makes tCheck useful for function input and return values. | |
* | |
* @example | |
* const example = (var1, var2) => { | |
* tCheck(var1, var2)(t.String, t.String); | |
* | |
* const result = var1 + var2; | |
* | |
* return tCheck(result)(t.String); | |
* }; | |
* | |
* example('foo', 'bar'); // foobar | |
* | |
* | |
* @throws {Error} If the values or args are empty, or are different lengths, | |
* or if the given types are not valid tcomb types. | |
* @throws {TypeError} If the values do not match the expected types. | |
* | |
* @return {value(s)} The single value if a single value was given, else | |
* multiple values are returned within an Array. | |
*/ | |
const tCheck = function values() { | |
const valueArgs = arguments; | |
/** | |
* If running in production mode we shall disable the type checking for | |
* performance reasons. | |
*/ | |
if (process.env.NODE_ENV === 'production') { | |
return () => { | |
if (valueArgs.length === 0) { | |
return void 0; | |
} | |
const valueArgsArr = Array.prototype.slice.call(valueArgs); | |
return valueArgsArr.length > 1 ? valueArgsArr : valueArgsArr[0]; | |
}; | |
} | |
return function types() { | |
const typeArgs = arguments; | |
if (valueArgs.length === 0) { | |
throw new Error( | |
'No "values" provided to tCheck.' | |
); | |
} | |
if (typeArgs.length === 0) { | |
throw new Error( | |
'No "types" provided to tCheck.' | |
); | |
} | |
if (valueArgs.length !== typeArgs.length) { | |
throw new Error( | |
'The "values" and "types" provided to tCheck have different lengths.' | |
); | |
} | |
const typeArgsArr = Array.prototype.slice.call(typeArgs); | |
typeArgsArr.forEach((x) => { | |
if (!isType(x)) { | |
throw new Error( | |
'The "types" provied to tCheck should be valid "tcomb" types.' | |
); | |
} | |
}); | |
const valueArgsArr = Array.prototype.slice.call(valueArgs); | |
for (let i = 0; i < valueArgsArr.length; i++) { | |
typeArgsArr[i](valueArgsArr[i]); | |
} | |
if (valueArgsArr.length === 1) { | |
return valueArgsArr[0]; | |
} | |
return valueArgsArr; | |
}; | |
}; | |
export default tCheck; |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import { expect } from 'chai'; | |
import t from 'tcomb'; | |
describe(`Given the tCheck util`, () => { | |
describe('When running in a non-production environment', () => { | |
let tCheck; | |
before(() => { | |
process.env.NODE_ENV = 'test'; | |
tCheck = require('./tCheck.js').default; | |
}); | |
it('Should fail for no value arguments', () => { | |
expect(() => tCheck()(t.String)).to.throw(Error, /No "values" provided/); | |
}); | |
it('Should fail for no type arguments', () => { | |
expect(() => tCheck('foo')()).to.throw(Error, /No "types" provided/); | |
}); | |
it('Should fail for arguments of different lengths', () => { | |
expect(() => tCheck('foo', true)(t.String)).to.throw(Error, /different lengths/); | |
}); | |
it('Should fail for type arguments that not valid tcomb types', () => { | |
expect(() => tCheck('foo')({})).to.throw(Error, /should be valid "tcomb" types/); | |
}); | |
it('Should fail when values are not of the expected type', () => { | |
expect(() => tCheck('foo')(t.Number)).to.throw(TypeError); | |
}); | |
it('Should fail when values are not of the expected type (n args)', () => { | |
expect(() => tCheck('foo', 1)(t.String, t.String)).to.throw(TypeError); | |
}); | |
it('Should return the actual value when a valid tCheck is performed for a single value', () => { | |
expect(tCheck('foo')(t.String)).to.equal('foo'); | |
}); | |
it('Should return an Array containing the values when a valid tCheck is performed for multiple values', () => { | |
const result = tCheck('foo', 1)(t.String, t.Number); | |
expect(result).to.be.an.instanceof(Array); | |
expect(result[0]).to.equal('foo'); | |
expect(result[1]).to.equal(1); | |
}); | |
it('Example correct implementation should return the correct result', () => { | |
const example = (var1, var2) => { | |
tCheck(var1, var2)(t.String, t.String); | |
const result = var1 + var2; | |
return tCheck(result)(t.String); | |
}; | |
const result = example('foo', 'bar'); | |
expect(result).to.equal('foobar'); | |
}); | |
}); | |
describe('When running in a production environment', () => { | |
let tCheck; | |
before(() => { | |
process.env.NODE_ENV = 'production'; | |
tCheck = require('./tCheck.js').default; | |
}); | |
it('Should not fail for no value arguments', () => { | |
expect(() => tCheck()(t.String)).to.not.throw(Error); | |
}); | |
it('Should not fail for no type arguments', () => { | |
expect(() => tCheck('foo')()).to.not.throw(Error); | |
}); | |
it('Should not fail for arguments of different lengths', () => { | |
expect(() => tCheck('foo', true)(t.String)).to.not.throw(Error); | |
}); | |
it('Should not fail for type arguments that not valid tcomb types', () => { | |
expect(() => tCheck('foo')({})).to.not.throw(Error); | |
}); | |
it('Should not fail when values are not of the expected type', () => { | |
expect(() => tCheck('foo')(t.Number)).to.not.throw(TypeError); | |
}); | |
it('Should not fail when values are not of the expected type (n args)', () => { | |
expect(() => tCheck('foo', 1)(t.String, t.String)).to.not.throw(TypeError); | |
}); | |
it('Should return undefined when no value is provided', () => { | |
expect(tCheck()()).to.equal(undefined); | |
}); | |
it('Should return the actual value when a single value is provided', () => { | |
expect(tCheck('foo')()).to.equal('foo'); | |
}); | |
it('Should return an Array containing the values when multiple values are provided', () => { | |
const result = tCheck('foo', 1)(); | |
expect(result).to.be.an.instanceof(Array); | |
expect(result[0]).to.equal('foo'); | |
expect(result[1]).to.equal(1); | |
}); | |
}); | |
}); |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment