Repository: kelektiv/node-cron
Pull request #690 (2023-08-16 13:08:35)

test: add TS types check
## Description
Imported and adapted the [types test file from `@types/cron`](https://github.com/DefinitelyTyped/DefinitelyTyped/blob/34872661a2fd0a6a43fe8da464bc2ec07ad49169/types/cron/cron-tests.ts).

*Note: The tests won't pass until #689 is merged since they detect the issue it fixes.*

## Related Issue
- #688

## Motivation and Context
This adds more safety to our workflow regarding TypeScript types. Note that this only statically checks the types, so there is no assurance the runtime types would match.

## Types of changes
- [ ] Bug fix (non-breaking change which fixes an issue)
- [ ] New feature (non-breaking change which adds functionality)
- [ ] Breaking change (fix or feature that would cause existing functionality to change)

## Checklist:
- [X] My code follows the code style of this project.
- [ ] My change requires a change to the documentation.
- [ ] I have updated the documentation accordingly.
- [ ] I have added tests to cover my changes.
- [ ] All new and existing tests passed.
- [ ] If my change introduces a breaking change, I have added a `!` after the type/scope in the title (see the Conventional Commits standard).



Patch:
diff --git a/lib/time.js b/lib/time.js
index 3b9e54c..5c4a98a 100644
--- a/lib/time.js
+++ b/lib/time.js
@@ -768,7 +768,7 @@ function CronTime(luxon) {
 				if (allRanges[i].match(RE_RANGE)) {
 					allRanges[i].replace(RE_RANGE, ($0, lower, upper, step) => {
 						lower = parseInt(lower, 10);
-						upper = parseInt(upper, 10) || undefined;
+						upper = upper !== undefined ? parseInt(upper, 10) : undefined;
 
 						const wasStepDefined = !isNaN(parseInt(step, 10));
 						if (step === '0') {
@@ -776,14 +776,14 @@ function CronTime(luxon) {
 						}
 						step = parseInt(step, 10) || 1;
 
-						if (upper && lower > upper) {
+						if (upper !== undefined && lower > upper) {
 							throw new Error(`Field (${type}) has an invalid range`);
 						}
 
 						const outOfRangeError =
 							lower < low ||
-							(upper && upper > high) ||
-							(!upper && lower > high);
+							(upper !== undefined && upper > high) ||
+							(upper === undefined && lower > high);
 
 						if (outOfRangeError) {
 							throw new Error(`Field value (${value}) is out of range`);
@@ -793,7 +793,7 @@ function CronTime(luxon) {
 						lower = Math.min(Math.max(low, ~~Math.abs(lower)), high);
 
 						// Positive integer lower than constraints[1]
-						if (upper) {
+						if (upper !== undefined) {
 							upper = Math.min(high, ~~Math.abs(upper));
 						} else {
 							// If step is provided, the default upper range is the highest value
diff --git a/package-lock.json b/package-lock.json
index 290953a..a8bfb7a 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -35,7 +35,8 @@
 				"jest": "~29.5.x",
 				"prettier": "~2.8.x",
 				"semantic-release": "~21.0.x",
-				"sinon": "^15.0.x"
+				"sinon": "^15.0.x",
+				"tsd": "^0.28.1"
 			}
 		},
 		"node_modules/@ampproject/remapping": {
@@ -2185,6 +2186,12 @@
 			"integrity": "sha512-vxhUy4J8lyeyinH7Azl1pdd43GJhZH/tP2weN8TntQblOY+A0XbT8DJk1/oCPuOOyg/Ja757rG0CgHcWC8OfMA==",
 			"dev": true
 		},
+		"node_modules/@tsd/typescript": {
+			"version": "5.0.4",
+			"resolved": "https://registry.npmjs.org/@tsd/typescript/-/typescript-5.0.4.tgz",
+			"integrity": "sha512-YQi2lvZSI+xidKeUjlbv6b6Zw7qB3aXHw5oGJLs5OOGAEqKIOvz5UIAkWyg0bJbkSUWPBEtaOHpVxU4EYBO1Jg==",
+			"dev": true
+		},
 		"node_modules/@types/babel__core": {
 			"version": "7.20.0",
 			"resolved": "https://registry.npmjs.org/@types/babel__core/-/babel__core-7.20.0.tgz",
@@ -2226,6 +2233,22 @@
 				"@babel/types": "^7.3.0"
 			}
 		},
+		"node_modules/@types/eslint": {
+			"version": "7.29.0",
+			"resolved": "https://registry.npmjs.org/@types/eslint/-/eslint-7.29.0.tgz",
+			"integrity": "sha512-VNcvioYDH8/FxaeTKkM4/TiTwt6pBV9E3OfGmvaw8tPl0rrHCJ4Ll15HRT+pMiFAf/MLQvAzC+6RzUMEL9Ceng==",
+			"dev": true,
+			"dependencies": {
+				"@types/estree": "*",
+				"@types/json-schema": "*"
+			}
+		},
+		"node_modules/@types/estree": {
+			"version": "1.0.1",
+			"resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.1.tgz",
+			"integrity": "sha512-LG4opVs2ANWZ1TJoKc937iMmNstM/d0ae1vNbnBvBhqCSezgVUOzcLCqbI5elV8Vy6WKwKjaqR+zO9VKirBBCA==",
+			"dev": true
+		},
 		"node_modules/@types/graceful-fs": {
 			"version": "4.1.6",
 			"resolved": "https://registry.npmjs.org/@types/graceful-fs/-/graceful-fs-4.1.6.tgz",
@@ -3914,6 +3937,28 @@
 				"eslint-plugin-promise": "^6.0.0"
 			}
 		},
+		"node_modules/eslint-formatter-pretty": {
+			"version": "4.1.0",
+			"resolved": "https://registry.npmjs.org/eslint-formatter-pretty/-/eslint-formatter-pretty-4.1.0.tgz",
+			"integrity": "sha512-IsUTtGxF1hrH6lMWiSl1WbGaiP01eT6kzywdY1U+zLc0MP+nwEnUiS9UI8IaOTUhTeQJLlCEWIbXINBH4YJbBQ==",
+			"dev": true,
+			"dependencies": {
+				"@types/eslint": "^7.2.13",
+				"ansi-escapes": "^4.2.1",
+				"chalk": "^4.1.0",
+				"eslint-rule-docs": "^1.1.5",
+				"log-symbols": "^4.0.0",
+				"plur": "^4.0.0",
+				"string-width": "^4.2.0",
+				"supports-hyperlinks": "^2.0.0"
+			},
+			"engines": {
+				"node": ">=10"
+			},
+			"funding": {
+				"url": "https://github.com/sponsors/sindresorhus"
+			}
+		},
 		"node_modules/eslint-import-resolver-node": {
 			"version": "0.3.7",
 			"dev": true,
@@ -4127,6 +4172,12 @@
 				"eslint": "^7.0.0 || ^8.0.0"
 			}
 		},
+		"node_modules/eslint-rule-docs": {
+			"version": "1.1.235",
+			"resolved": "https://registry.npmjs.org/eslint-rule-docs/-/eslint-rule-docs-1.1.235.tgz",
+			"integrity": "sha512-+TQ+x4JdTnDoFEXXb3fDvfGOwnyNV7duH8fXWTPD1ieaBmB8omj7Gw/pMBBu4uI2uJCCU8APDaQJzWuXnTsH4A==",
+			"dev": true
+		},
 		"node_modules/eslint-scope": {
 			"version": "7.1.1",
 			"dev": true,
@@ -5111,6 +5162,15 @@
 				"url": "https://github.com/sponsors/sindresorhus"
 			}
 		},
+		"node_modules/irregular-plurals": {
+			"version": "3.5.0",
+			"resolved": "https://registry.npmjs.org/irregular-plurals/-/irregular-plurals-3.5.0.tgz",
+			"integrity": "sha512-1ANGLZ+Nkv1ptFb2pa8oG8Lem4krflKuX/gINiHJHjJUKaJHk/SXk5x6K3J+39/p0h1RQ2saROclJJ+QLvETCQ==",
+			"dev": true,
+			"engines": {
+				"node": ">=8"
+			}
+		},
 		"node_modules/is-array-buffer": {
 			"version": "3.0.2",
 			"dev": true,
@@ -6417,6 +6477,34 @@
 			"integrity": "sha512-sReKOYJIJf74dhJONhU4e0/shzi1trVbSWDOhKYE5XV2O+H7Sb2Dihwuc7xWxVl+DgFPyTqIN3zMfT9cq5iWDg==",
 			"dev": true
 		},
+		"node_modules/log-symbols": {
+			"version": "4.1.0",
+			"resolved": "https://registry.npmjs.org/log-symbols/-/log-symbols-4.1.0.tgz",
+			"integrity": "sha512-8XPvpAA8uyhfteu8pIvQxpJZ7SYYdpUivZpGy6sFsBuKRY/7rQGavedeB8aK+Zkyq6upMFVL/9AW6vOYzfRyLg==",
+			"dev": true,
+			"dependencies": {
+				"chalk": "^4.1.0",
+				"is-unicode-supported": "^0.1.0"
+			},
+			"engines": {
+				"node": ">=10"
+			},
+			"funding": {
+				"url": "https://github.com/sponsors/sindresorhus"
+			}
+		},
+		"node_modules/log-symbols/node_modules/is-unicode-supported": {
+			"version": "0.1.0",
+			"resolved": "https://registry.npmjs.org/is-unicode-supported/-/is-unicode-supported-0.1.0.tgz",
+			"integrity": "sha512-knxG2q4UC3u8stRGyAVJCOdxFmv5DZiRcdlIaAQXAbSfJya+OhopNotLQrstBhququ4ZpuKbDc/8S6mgXgPFPw==",
+			"dev": true,
+			"engines": {
+				"node": ">=10"
+			},
+			"funding": {
+				"url": "https://github.com/sponsors/sindresorhus"
+			}
+		},
 		"node_modules/lru-cache": {
 			"version": "6.0.0",
 			"dev": true,
@@ -10701,6 +10789,21 @@
 				"node": ">=8"
 			}
 		},
+		"node_modules/plur": {
+			"version": "4.0.0",
+			"resolved": "https://registry.npmjs.org/plur/-/plur-4.0.0.tgz",
+			"integrity": "sha512-4UGewrYgqDFw9vV6zNV+ADmPAUAfJPKtGvb/VdpQAx25X5f3xXdGdyOEVFwkl8Hl/tl7+xbeHqSEM+D5/TirUg==",
+			"dev": true,
+			"dependencies": {
+				"irregular-plurals": "^3.2.0"
+			},
+			"engines": {
+				"node": ">=10"
+			},
+			"funding": {
+				"url": "https://github.com/sponsors/sindresorhus"
+			}
+		},
 		"node_modules/prelude-ls": {
 			"version": "1.2.1",
 			"dev": true,
@@ -12361,6 +12464,203 @@
 				"json5": "lib/cli.js"
 			}
 		},
+		"node_modules/tsd": {
+			"version": "0.28.1",
+			"resolved": "https://registry.npmjs.org/tsd/-/tsd-0.28.1.tgz",
+			"integrity": "sha512-FeYrfJ05QgEMW/qOukNCr4fAJHww4SaKnivAXRv4g5kj4FeLpNV7zH4dorzB9zAfVX4wmA7zWu/wQf7kkcvfbw==",
+			"dev": true,
+			"dependencies": {
+				"@tsd/typescript": "~5.0.2",
+				"eslint-formatter-pretty": "^4.1.0",
+				"globby": "^11.0.1",
+				"jest-diff": "^29.0.3",
+				"meow": "^9.0.0",
+				"path-exists": "^4.0.0",
+				"read-pkg-up": "^7.0.0"
+			},
+			"bin": {
+				"tsd": "dist/cli.js"
+			},
+			"engines": {
+				"node": ">=14.16"
+			}
+		},
+		"node_modules/tsd/node_modules/find-up": {
+			"version": "4.1.0",
+			"resolved": "https://registry.npmjs.org/find-up/-/find-up-4.1.0.tgz",
+			"integrity": "sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==",
+			"dev": true,
+			"dependencies": {
+				"locate-path": "^5.0.0",
+				"path-exists": "^4.0.0"
+			},
+			"engines": {
+				"node": ">=8"
+			}
+		},
+		"node_modules/tsd/node_modules/hosted-git-info": {
+			"version": "2.8.9",
+			"resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-2.8.9.tgz",
+			"integrity": "sha512-mxIDAb9Lsm6DoOJ7xH+5+X4y1LU/4Hi50L9C5sIswK3JzULS4bwk1FvjdBgvYR4bzT4tuUQiC15FE2f5HbLvYw==",
+			"dev": true
+		},
+		"node_modules/tsd/node_modules/locate-path": {
+			"version": "5.0.0",
+			"resolved": "https://registry.npmjs.org/locate-path/-/locate-path-5.0.0.tgz",
+			"integrity": "sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==",
+			"dev": true,
+			"dependencies": {
+				"p-locate": "^4.1.0"
+			},
+			"engines": {
+				"node": ">=8"
+			}
+		},
+		"node_modules/tsd/node_modules/meow": {
+			"version": "9.0.0",
+			"resolved": "https://registry.npmjs.org/meow/-/meow-9.0.0.tgz",
+			"integrity": "sha512-+obSblOQmRhcyBt62furQqRAQpNyWXo8BuQ5bN7dG8wmwQ+vwHKp/rCFD4CrTP8CsDQD1sjoZ94K417XEUk8IQ==",
+			"dev": true,
+			"dependencies": {
+				"@types/minimist": "^1.2.0",
+				"camelcase-keys": "^6.2.2",
+				"decamelize": "^1.2.0",
+				"decamelize-keys": "^1.1.0",
+				"hard-rejection": "^2.1.0",
+				"minimist-options": "4.1.0",
+				"normalize-package-data": "^3.0.0",
+				"read-pkg-up": "^7.0.1",
+				"redent": "^3.0.0",
+				"trim-newlines": "^3.0.0",
+				"type-fest": "^0.18.0",
+				"yargs-parser": "^20.2.3"
+			},
+			"engines": {
+				"node": ">=10"
+			},
+			"funding": {
+				"url": "https://github.com/sponsors/sindresorhus"
+			}
+		},
+		"node_modules/tsd/node_modules/p-limit": {
+			"version": "2.3.0",
+			"resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz",
+			"integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==",
+			"dev": true,
+			"dependencies": {
+				"p-try": "^2.0.0"
+			},
+			"engines": {
+				"node": ">=6"
+			},
+			"funding": {
+				"url": "https://github.com/sponsors/sindresorhus"
+			}
+		},
+		"node_modules/tsd/node_modules/p-locate": {
+			"version": "4.1.0",
+			"resolved": "https://registry.npmjs.org/p-locate/-/p-locate-4.1.0.tgz",
+			"integrity": "sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==",
+			"dev": true,
+			"dependencies": {
+				"p-limit": "^2.2.0"
+			},
+			"engines": {
+				"node": ">=8"
+			}
+		},
+		"node_modules/tsd/node_modules/read-pkg": {
+			"version": "5.2.0",
+			"resolved": "https://registry.npmjs.org/read-pkg/-/read-pkg-5.2.0.tgz",
+			"integrity": "sha512-Ug69mNOpfvKDAc2Q8DRpMjjzdtrnv9HcSMX+4VsZxD1aZ6ZzrIE7rlzXBtWTyhULSMKg076AW6WR5iZpD0JiOg==",
+			"dev": true,
+			"dependencies": {
+				"@types/normalize-package-data": "^2.4.0",
+				"normalize-package-data": "^2.5.0",
+				"parse-json": "^5.0.0",
+				"type-fest": "^0.6.0"
+			},
+			"engines": {
+				"node": ">=8"
+			}
+		},
+		"node_modules/tsd/node_modules/read-pkg-up": {
+			"version": "7.0.1",
+			"resolved": "https://registry.npmjs.org/read-pkg-up/-/read-pkg-up-7.0.1.tgz",
+			"integrity": "sha512-zK0TB7Xd6JpCLmlLmufqykGE+/TlOePD6qKClNW7hHDKFh/J7/7gCWGR7joEQEW1bKq3a3yUZSObOoWLFQ4ohg==",
+			"dev": true,
+			"dependencies": {
+				"find-up": "^4.1.0",
+				"read-pkg": "^5.2.0",
+				"type-fest": "^0.8.1"
+			},
+			"engines": {
+				"node": ">=8"
+			},
+			"funding": {
+				"url": "https://github.com/sponsors/sindresorhus"
+			}
+		},
+		"node_modules/tsd/node_modules/read-pkg-up/node_modules/type-fest": {
+			"version": "0.8.1",
+			"resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.8.1.tgz",
+			"integrity": "sha512-4dbzIzqvjtgiM5rw1k5rEHtBANKmdudhGyBEajN01fEyhaAIhsoKNy6y7+IN93IfpFtwY9iqi7kD+xwKhQsNJA==",
+			"dev": true,
+			"engines": {
+				"node": ">=8"
+			}
+		},
+		"node_modules/tsd/node_modules/read-pkg/node_modules/normalize-package-data": {
+			"version": "2.5.0",
+			"resolved": "https://registry.npmjs.org/normalize-package-data/-/normalize-package-data-2.5.0.tgz",
+			"integrity": "sha512-/5CMN3T0R4XTj4DcGaexo+roZSdSFW/0AOOTROrjxzCG1wrWXEsGbRKevjlIL+ZDE4sZlJr5ED4YW0yqmkK+eA==",
+			"dev": true,
+			"dependencies": {
+				"hosted-git-info": "^2.1.4",
+				"resolve": "^1.10.0",
+				"semver": "2 || 3 || 4 || 5",
+				"validate-npm-package-license": "^3.0.1"
+			}
+		},
+		"node_modules/tsd/node_modules/read-pkg/node_modules/type-fest": {
+			"version": "0.6.0",
+			"resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.6.0.tgz",
+			"integrity": "sha512-q+MB8nYR1KDLrgr4G5yemftpMC7/QLqVndBmEEdqzmNj5dcFOO4Oo8qlwZE3ULT3+Zim1F8Kq4cBnikNhlCMlg==",
+			"dev": true,
+			"engines": {
+				"node": ">=8"
+			}
+		},
+		"node_modules/tsd/node_modules/semver": {
+			"version": "5.7.2",
+			"resolved": "https://registry.npmjs.org/semver/-/semver-5.7.2.tgz",
+			"integrity": "sha512-cBznnQ9KjJqU67B52RMC65CMarK2600WFnbkcaiwWq3xy/5haFJlshgnpjovMVJ+Hff49d8GEn0b87C5pDQ10g==",
+			"dev": true,
+			"bin": {
+				"semver": "bin/semver"
+			}
+		},
+		"node_modules/tsd/node_modules/type-fest": {
+			"version": "0.18.1",
+			"resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.18.1.tgz",
+			"integrity": "sha512-OIAYXk8+ISY+qTOwkHtKqzAuxchoMiD9Udx+FSGQDuiRR+PJKJHc2NJAXlbhkGwTt/4/nKZxELY1w3ReWOL8mw==",
+			"dev": true,
+			"engines": {
+				"node": ">=10"
+			},
+			"funding": {
+				"url": "https://github.com/sponsors/sindresorhus"
+			}
+		},
+		"node_modules/tsd/node_modules/yargs-parser": {
+			"version": "20.2.9",
+			"resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-20.2.9.tgz",
+			"integrity": "sha512-y11nGElTIV+CT3Zv9t7VKl+Q3hTQoT9a1Qzezhhl6Rp21gJ/IVTW7Z3y9EWXhuUBC2Shnf+DX0antecpAwSP8w==",
+			"dev": true,
+			"engines": {
+				"node": ">=10"
+			}
+		},
 		"node_modules/tslib": {
 			"version": "1.14.1",
 			"dev": true,
@@ -14400,6 +14700,12 @@
 			"integrity": "sha512-vxhUy4J8lyeyinH7Azl1pdd43GJhZH/tP2weN8TntQblOY+A0XbT8DJk1/oCPuOOyg/Ja757rG0CgHcWC8OfMA==",
 			"dev": true
 		},
+		"@tsd/typescript": {
+			"version": "5.0.4",
+			"resolved": "https://registry.npmjs.org/@tsd/typescript/-/typescript-5.0.4.tgz",
+			"integrity": "sha512-YQi2lvZSI+xidKeUjlbv6b6Zw7qB3aXHw5oGJLs5OOGAEqKIOvz5UIAkWyg0bJbkSUWPBEtaOHpVxU4EYBO1Jg==",
+			"dev": true
+		},
 		"@types/babel__core": {
 			"version": "7.20.0",
 			"resolved": "https://registry.npmjs.org/@types/babel__core/-/babel__core-7.20.0.tgz",
@@ -14441,6 +14747,22 @@
 				"@babel/types": "^7.3.0"
 			}
 		},
+		"@types/eslint": {
+			"version": "7.29.0",
+			"resolved": "https://registry.npmjs.org/@types/eslint/-/eslint-7.29.0.tgz",
+			"integrity": "sha512-VNcvioYDH8/FxaeTKkM4/TiTwt6pBV9E3OfGmvaw8tPl0rrHCJ4Ll15HRT+pMiFAf/MLQvAzC+6RzUMEL9Ceng==",
+			"dev": true,
+			"requires": {
+				"@types/estree": "*",
+				"@types/json-schema": "*"
+			}
+		},
+		"@types/estree": {
+			"version": "1.0.1",
+			"resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.1.tgz",
+			"integrity": "sha512-LG4opVs2ANWZ1TJoKc937iMmNstM/d0ae1vNbnBvBhqCSezgVUOzcLCqbI5elV8Vy6WKwKjaqR+zO9VKirBBCA==",
+			"dev": true
+		},
 		"@types/graceful-fs": {
 			"version": "4.1.6",
 			"resolved": "https://registry.npmjs.org/@types/graceful-fs/-/graceful-fs-4.1.6.tgz",
@@ -15638,6 +15960,22 @@
 			"dev": true,
 			"requires": {}
 		},
+		"eslint-formatter-pretty": {
+			"version": "4.1.0",
+			"resolved": "https://registry.npmjs.org/eslint-formatter-pretty/-/eslint-formatter-pretty-4.1.0.tgz",
+			"integrity": "sha512-IsUTtGxF1hrH6lMWiSl1WbGaiP01eT6kzywdY1U+zLc0MP+nwEnUiS9UI8IaOTUhTeQJLlCEWIbXINBH4YJbBQ==",
+			"dev": true,
+			"requires": {
+				"@types/eslint": "^7.2.13",
+				"ansi-escapes": "^4.2.1",
+				"chalk": "^4.1.0",
+				"eslint-rule-docs": "^1.1.5",
+				"log-symbols": "^4.0.0",
+				"plur": "^4.0.0",
+				"string-width": "^4.2.0",
+				"supports-hyperlinks": "^2.0.0"
+			}
+		},
 		"eslint-import-resolver-node": {
 			"version": "0.3.7",
 			"dev": true,
@@ -15768,6 +16106,12 @@
 			"dev": true,
 			"requires": {}
 		},
+		"eslint-rule-docs": {
+			"version": "1.1.235",
+			"resolved": "https://registry.npmjs.org/eslint-rule-docs/-/eslint-rule-docs-1.1.235.tgz",
+			"integrity": "sha512-+TQ+x4JdTnDoFEXXb3fDvfGOwnyNV7duH8fXWTPD1ieaBmB8omj7Gw/pMBBu4uI2uJCCU8APDaQJzWuXnTsH4A==",
+			"dev": true
+		},
 		"eslint-scope": {
 			"version": "7.1.1",
 			"dev": true,
@@ -16392,6 +16736,12 @@
 				"p-is-promise": "^3.0.0"
 			}
 		},
+		"irregular-plurals": {
+			"version": "3.5.0",
+			"resolved": "https://registry.npmjs.org/irregular-plurals/-/irregular-plurals-3.5.0.tgz",
+			"integrity": "sha512-1ANGLZ+Nkv1ptFb2pa8oG8Lem4krflKuX/gINiHJHjJUKaJHk/SXk5x6K3J+39/p0h1RQ2saROclJJ+QLvETCQ==",
+			"dev": true
+		},
 		"is-array-buffer": {
 			"version": "3.0.2",
 			"dev": true,
@@ -17355,6 +17705,24 @@
 			"integrity": "sha512-sReKOYJIJf74dhJONhU4e0/shzi1trVbSWDOhKYE5XV2O+H7Sb2Dihwuc7xWxVl+DgFPyTqIN3zMfT9cq5iWDg==",
 			"dev": true
 		},
+		"log-symbols": {
+			"version": "4.1.0",
+			"resolved": "https://registry.npmjs.org/log-symbols/-/log-symbols-4.1.0.tgz",
+			"integrity": "sha512-8XPvpAA8uyhfteu8pIvQxpJZ7SYYdpUivZpGy6sFsBuKRY/7rQGavedeB8aK+Zkyq6upMFVL/9AW6vOYzfRyLg==",
+			"dev": true,
+			"requires": {
+				"chalk": "^4.1.0",
+				"is-unicode-supported": "^0.1.0"
+			},
+			"dependencies": {
+				"is-unicode-supported": {
+					"version": "0.1.0",
+					"resolved": "https://registry.npmjs.org/is-unicode-supported/-/is-unicode-supported-0.1.0.tgz",
+					"integrity": "sha512-knxG2q4UC3u8stRGyAVJCOdxFmv5DZiRcdlIaAQXAbSfJya+OhopNotLQrstBhququ4ZpuKbDc/8S6mgXgPFPw==",
+					"dev": true
+				}
+			}
+		},
 		"lru-cache": {
 			"version": "6.0.0",
 			"dev": true,
@@ -20263,6 +20631,15 @@
 				}
 			}
 		},
+		"plur": {
+			"version": "4.0.0",
+			"resolved": "https://registry.npmjs.org/plur/-/plur-4.0.0.tgz",
+			"integrity": "sha512-4UGewrYgqDFw9vV6zNV+ADmPAUAfJPKtGvb/VdpQAx25X5f3xXdGdyOEVFwkl8Hl/tl7+xbeHqSEM+D5/TirUg==",
+			"dev": true,
+			"requires": {
+				"irregular-plurals": "^3.2.0"
+			}
+		},
 		"prelude-ls": {
 			"version": "1.2.1",
 			"dev": true
@@ -21415,6 +21792,155 @@
 				}
 			}
 		},
+		"tsd": {
+			"version": "0.28.1",
+			"resolved": "https://registry.npmjs.org/tsd/-/tsd-0.28.1.tgz",
+			"integrity": "sha512-FeYrfJ05QgEMW/qOukNCr4fAJHww4SaKnivAXRv4g5kj4FeLpNV7zH4dorzB9zAfVX4wmA7zWu/wQf7kkcvfbw==",
+			"dev": true,
+			"requires": {
+				"@tsd/typescript": "~5.0.2",
+				"eslint-formatter-pretty": "^4.1.0",
+				"globby": "^11.0.1",
+				"jest-diff": "^29.0.3",
+				"meow": "^9.0.0",
+				"path-exists": "^4.0.0",
+				"read-pkg-up": "^7.0.0"
+			},
+			"dependencies": {
+				"find-up": {
+					"version": "4.1.0",
+					"resolved": "https://registry.npmjs.org/find-up/-/find-up-4.1.0.tgz",
+					"integrity": "sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==",
+					"dev": true,
+					"requires": {
+						"locate-path": "^5.0.0",
+						"path-exists": "^4.0.0"
+					}
+				},
+				"hosted-git-info": {
+					"version": "2.8.9",
+					"resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-2.8.9.tgz",
+					"integrity": "sha512-mxIDAb9Lsm6DoOJ7xH+5+X4y1LU/4Hi50L9C5sIswK3JzULS4bwk1FvjdBgvYR4bzT4tuUQiC15FE2f5HbLvYw==",
+					"dev": true
+				},
+				"locate-path": {
+					"version": "5.0.0",
+					"resolved": "https://registry.npmjs.org/locate-path/-/locate-path-5.0.0.tgz",
+					"integrity": "sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==",
+					"dev": true,
+					"requires": {
+						"p-locate": "^4.1.0"
+					}
+				},
+				"meow": {
+					"version": "9.0.0",
+					"resolved": "https://registry.npmjs.org/meow/-/meow-9.0.0.tgz",
+					"integrity": "sha512-+obSblOQmRhcyBt62furQqRAQpNyWXo8BuQ5bN7dG8wmwQ+vwHKp/rCFD4CrTP8CsDQD1sjoZ94K417XEUk8IQ==",
+					"dev": true,
+					"requires": {
+						"@types/minimist": "^1.2.0",
+						"camelcase-keys": "^6.2.2",
+						"decamelize": "^1.2.0",
+						"decamelize-keys": "^1.1.0",
+						"hard-rejection": "^2.1.0",
+						"minimist-options": "4.1.0",
+						"normalize-package-data": "^3.0.0",
+						"read-pkg-up": "^7.0.1",
+						"redent": "^3.0.0",
+						"trim-newlines": "^3.0.0",
+						"type-fest": "^0.18.0",
+						"yargs-parser": "^20.2.3"
+					}
+				},
+				"p-limit": {
+					"version": "2.3.0",
+					"resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz",
+					"integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==",
+					"dev": true,
+					"requires": {
+						"p-try": "^2.0.0"
+					}
+				},
+				"p-locate": {
+					"version": "4.1.0",
+					"resolved": "https://registry.npmjs.org/p-locate/-/p-locate-4.1.0.tgz",
+					"integrity": "sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==",
+					"dev": true,
+					"requires": {
+						"p-limit": "^2.2.0"
+					}
+				},
+				"read-pkg": {
+					"version": "5.2.0",
+					"resolved": "https://registry.npmjs.org/read-pkg/-/read-pkg-5.2.0.tgz",
+					"integrity": "sha512-Ug69mNOpfvKDAc2Q8DRpMjjzdtrnv9HcSMX+4VsZxD1aZ6ZzrIE7rlzXBtWTyhULSMKg076AW6WR5iZpD0JiOg==",
+					"dev": true,
+					"requires": {
+						"@types/normalize-package-data": "^2.4.0",
+						"normalize-package-data": "^2.5.0",
+						"parse-json": "^5.0.0",
+						"type-fest": "^0.6.0"
+					},
+					"dependencies": {
+						"normalize-package-data": {
+							"version": "2.5.0",
+							"resolved": "https://registry.npmjs.org/normalize-package-data/-/normalize-package-data-2.5.0.tgz",
+							"integrity": "sha512-/5CMN3T0R4XTj4DcGaexo+roZSdSFW/0AOOTROrjxzCG1wrWXEsGbRKevjlIL+ZDE4sZlJr5ED4YW0yqmkK+eA==",
+							"dev": true,
+							"requires": {
+								"hosted-git-info": "^2.1.4",
+								"resolve": "^1.10.0",
+								"semver": "2 || 3 || 4 || 5",
+								"validate-npm-package-license": "^3.0.1"
+							}
+						},
+						"type-fest": {
+							"version": "0.6.0",
+							"resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.6.0.tgz",
+							"integrity": "sha512-q+MB8nYR1KDLrgr4G5yemftpMC7/QLqVndBmEEdqzmNj5dcFOO4Oo8qlwZE3ULT3+Zim1F8Kq4cBnikNhlCMlg==",
+							"dev": true
+						}
+					}
+				},
+				"read-pkg-up": {
+					"version": "7.0.1",
+					"resolved": "https://registry.npmjs.org/read-pkg-up/-/read-pkg-up-7.0.1.tgz",
+					"integrity": "sha512-zK0TB7Xd6JpCLmlLmufqykGE+/TlOePD6qKClNW7hHDKFh/J7/7gCWGR7joEQEW1bKq3a3yUZSObOoWLFQ4ohg==",
+					"dev": true,
+					"requires": {
+						"find-up": "^4.1.0",
+						"read-pkg": "^5.2.0",
+						"type-fest": "^0.8.1"
+					},
+					"dependencies": {
+						"type-fest": {
+							"version": "0.8.1",
+							"resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.8.1.tgz",
+							"integrity": "sha512-4dbzIzqvjtgiM5rw1k5rEHtBANKmdudhGyBEajN01fEyhaAIhsoKNy6y7+IN93IfpFtwY9iqi7kD+xwKhQsNJA==",
+							"dev": true
+						}
+					}
+				},
+				"semver": {
+					"version": "5.7.2",
+					"resolved": "https://registry.npmjs.org/semver/-/semver-5.7.2.tgz",
+					"integrity": "sha512-cBznnQ9KjJqU67B52RMC65CMarK2600WFnbkcaiwWq3xy/5haFJlshgnpjovMVJ+Hff49d8GEn0b87C5pDQ10g==",
+					"dev": true
+				},
+				"type-fest": {
+					"version": "0.18.1",
+					"resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.18.1.tgz",
+					"integrity": "sha512-OIAYXk8+ISY+qTOwkHtKqzAuxchoMiD9Udx+FSGQDuiRR+PJKJHc2NJAXlbhkGwTt/4/nKZxELY1w3ReWOL8mw==",
+					"dev": true
+				},
+				"yargs-parser": {
+					"version": "20.2.9",
+					"resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-20.2.9.tgz",
+					"integrity": "sha512-y11nGElTIV+CT3Zv9t7VKl+Q3hTQoT9a1Qzezhhl6Rp21gJ/IVTW7Z3y9EWXhuUBC2Shnf+DX0antecpAwSP8w==",
+					"dev": true
+				}
+			}
+		},
 		"tslib": {
 			"version": "1.14.1",
 			"dev": true
diff --git a/package.json b/package.json
index 57de009..5ba601f 100644
--- a/package.json
+++ b/package.json
@@ -15,6 +15,7 @@
 		"lint": "eslint {lib,tests}/*.js",
 		"test": "jest --coverage",
 		"test:watch": "jest --watch --coverage",
+		"test:types": "tsd",
 		"prepare": "husky install",
 		"release": "semantic-release"
 	},
@@ -46,7 +47,8 @@
 		"jest": "~29.5.x",
 		"prettier": "~2.8.x",
 		"semantic-release": "~21.0.x",
-		"sinon": "^15.0.x"
+		"sinon": "^15.0.x",
+		"tsd": "^0.28.1"
 	},
 	"keywords": [
 		"cron",


Test patch:
diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml
index 7c503f7..42fd506 100644
--- a/.github/workflows/test.yml
+++ b/.github/workflows/test.yml
@@ -27,7 +27,7 @@ jobs:
         run: npm ci
       - name: Check codestyle compliance
         run: npm run lint
+      - name: Check TS types
+        run: npm run test:types
       - name: Run tests
         run: npm run test
-        env:
-          CI: true
diff --git a/tests/crontime.test.js b/tests/crontime.test.js
index 0d962d0..d6721bd 100644
--- a/tests/crontime.test.js
+++ b/tests/crontime.test.js
@@ -70,6 +70,12 @@ describe('crontime', () => {
 		}).not.toThrow();
 	});
 
+	it('should accept all valid ranges (0-59 0-59 1-23 1-31 0-12 0-6)', () => {
+		expect(() => {
+			new cron.CronTime('0-59 0-59 1-23 1-31 0-11 0-6');
+		}).not.toThrow();
+	});
+
 	it('should test comma (0,10 * * * * *)', () => {
 		expect(() => {
 			new cron.CronTime('0,10 * * * * *');
@@ -152,6 +158,14 @@ describe('crontime', () => {
 		expect(() => {
 			new cron.CronTime('* 2-1 * * *');
 		}).toThrow();
+
+		expect(() => {
+			new cron.CronTime('* 2-0 * * *');
+		}).toThrow();
+
+		expect(() => {
+			new cron.CronTime('* 2- * * *');
+		}).toThrow();
 	});
 
 	it('should test Date', () => {
diff --git a/types/index.test-d.ts b/types/index.test-d.ts
new file mode 100644
index 0000000..7e5f29b
--- /dev/null
+++ b/types/index.test-d.ts
@@ -0,0 +1,85 @@
+import * as luxon from 'luxon';
+import { expectType } from 'tsd';
+import * as cron from '..';
+
+const CronJob = cron.CronJob;
+const CronTime = cron.CronTime;
+
+const DateTime = luxon.DateTime;
+
+const timeZone = 'America/Los_Angeles';
+
+// basic cron usage
+const job = new CronJob(
+  '* * * * * *',
+  () => {},
+  null,
+  true
+);
+
+// using factory function with parameters object
+expectType<cron.CronJob>(cron.job({
+  cronTime: '00 30 11 * * 1-5',
+  onTick: () => {},
+  start: false,
+  timeZone: 'America/Los_Angeles'
+}));
+
+// with timezone
+new CronJob(
+  '* * * * * *',
+  () => {},
+  null,
+  true,
+  timeZone
+);
+
+// with onComplete
+new CronJob(
+  '00 30 11 * * 1-5',
+  () => {},
+  () => {},
+  true,
+  timeZone
+);
+
+// with Date
+new CronJob(
+  new Date(),
+  () => {},
+  null,
+  true,
+  timeZone
+);
+
+// with Luxon DateTime
+new CronJob(
+  DateTime.local(),
+  () => {},
+  null,
+  true,
+  timeZone
+);
+
+// with system commands
+new CronJob(
+  '00 30 11 * * 1-5',
+  'ls',
+  { command: 'ls', args: ['./'] },
+  true,
+  timeZone
+);
+
+// check function return types
+expectType<Date>(job.lastDate());
+expectType<luxon.DateTime>(job.nextDates());
+expectType<luxon.DateTime | luxon.DateTime[]>(job.nextDates(1));
+expectType<boolean | undefined>(job.running);
+job.setTime(new CronTime('00 30 11 * * 1-2'));
+job.start();
+job.stop();
+
+// check cronTime format
+new CronTime('* * * * * *');
+new CronTime(new Date());
+new CronTime(DateTime.local());