Repository: sindresorhus/is
Pull request #23 (2017-10-17 01:39:43)

Add is.even() and is.odd()
Intentionally left `even` and `odd` out of `types` in the tests for these ones. Integer / number values are growing in their need to be explicitly excluded from one another. Glad to add them if that's preferred. 



sindresorhus: > Intentionally left even and odd out of types in the tests for these ones. Integer / number values are growing in their need to be explicitly excluded from one another. 

That's ok.

Patch:
diff --git a/index.js b/index.js
index 0da91b3..d5abf64 100644
--- a/index.js
+++ b/index.js
@@ -124,6 +124,7 @@ const primitiveTypes = new Set([
 is.primitive = x => is.null(x) || primitiveTypes.has(typeof x);
 
 is.integer = Number.isInteger;
+is.safeInteger = Number.isSafeInteger;
 
 is.plainObject = x => {
 	// From: https://github.com/sindresorhus/is-plain-obj/blob/master/index.js
@@ -178,6 +179,10 @@ is.domElement = x => is.object(x) && x.nodeType === NODE_TYPE_ELEMENT && is.stri
 
 is.infinite = x => x === Infinity || x === -Infinity;
 
+const isAbsoluteMod2 = value => x => is.integer(x) && Math.abs(x % 2) === value;
+is.even = isAbsoluteMod2(0);
+is.odd = isAbsoluteMod2(1);
+
 const isEmptyStringOrArray = x => (is.string(x) || is.array(x)) && x.length === 0;
 const isEmptyObject = x => !is.map(x) && !is.set(x) && is.object(x) && Object.keys(x).length === 0;
 const isEmptyMapOrSet = x => (is.map(x) || is.set(x)) && x.size === 0;
diff --git a/readme.md b/readme.md
index 9e565fb..1760692 100644
--- a/readme.md
+++ b/readme.md
@@ -128,6 +128,11 @@ is.asyncFunction(() => {});
 JavaScript primitives are as follows: `null`, `undefined`, `string`, `number`, `boolean`, `symbol`.
 
 ##### .integer(value)
+
+##### .safeInteger(value)
+
+Returns `true` if `value` is a [safe integer](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Number/isSafeInteger).
+
 ##### .plainObject(value)
 
 An object is plain if it's created by either `{}`, `new Object()`, or `Object.create(null)`.
@@ -165,6 +170,14 @@ Returns `true` if `value` is a DOM Element.
 
 Check if `value` is `Infinity` or `-Infinity`.
 
+##### .even(value)
+
+Returns `true` if `value` is an even integer.
+
+##### .odd(value)
+
+Returns `true` if `value` is an odd integer.
+
 ##### .empty(value)
 
 Returns `true` if `value` is falsy or an empty string, array, object, map, or set.


Test patch:
diff --git a/test.js b/test.js
index 0d62dce..910f37a 100644
--- a/test.js
+++ b/test.js
@@ -98,6 +98,10 @@ const types = new Map([
 		new Object() // eslint-disable-line no-new-object
 	]],
 	['integer', 6],
+	['safeInteger', [
+		Math.pow(2, 53) - 1,
+		-Math.pow(2, 53) + 1
+	]],
 	['domElement', [
 		'div',
 		'input',
@@ -160,7 +164,7 @@ test('is.string', t => {
 });
 
 test('is.number', t => {
-	testType(t, 'number', ['nan', 'integer', 'infinite']);
+	testType(t, 'number', ['nan', 'integer', 'safeInteger', 'infinite']);
 });
 
 test('is.boolean', t => {
@@ -312,10 +316,16 @@ test('is.primitive', t => {
 });
 
 test('is.integer', t => {
-	testType(t, 'integer', ['number']);
+	testType(t, 'integer', ['number', 'safeInteger']);
 	t.false(m.integer(1.4));
 });
 
+test('is.safeInteger', t => {
+	testType(t, 'safeInteger', ['number', 'integer']);
+	t.false(m.safeInteger(Math.pow(2, 53)));
+	t.false(m.safeInteger(-Math.pow(2, 53)));
+});
+
 test('is.plainObject', t => {
 	testType(t, 'plainObject', ['object', 'promise']);
 });
@@ -409,6 +419,26 @@ test('is.infinite', t => {
 	testType(t, 'infinite', ['number']);
 });
 
+test('is.even', t => {
+	for (const el of [-6, 2, 4]) {
+		t.true(m.even(el));
+	}
+
+	for (const el of [-3, 1, 5]) {
+		t.false(m.even(el));
+	}
+});
+
+test('is.odd', t => {
+	for (const el of [-5, 7, 13]) {
+		t.true(m.odd(el));
+	}
+
+	for (const el of [-8, 8, 10]) {
+		t.false(m.odd(el));
+	}
+});
+
 test('is.empty', t => {
 	t.true(m.empty(null));
 	t.true(m.empty(undefined));