diff --git a/bench/isNumericString.bench.ts b/bench/isNumericString.bench.ts new file mode 100644 index 0000000..5da5db4 --- /dev/null +++ b/bench/isNumericString.bench.ts @@ -0,0 +1,24 @@ +import { bench } from 'vitest'; + +import { isNumericString } from '../src/predicate/isNumericString.js'; + +const numberValue = Number.MAX_SAFE_INTEGER; +const bigIntValue = BigInt(Number.MAX_VALUE) + BigInt(Number.MAX_VALUE); +const stringValue = String(numberValue); +const bigIntStringValue = String(bigIntValue); + +bench('isNumericString() - number', () => { + isNumericString(numberValue); +}); + +bench('isNumericString() - bigint', () => { + isNumericString(bigIntValue); +}); + +bench('isNumericString() - stringified number', () => { + isNumericString(stringValue); +}); + +bench('isNumericString() - stringified bigint', () => { + isNumericString(bigIntStringValue); +}); diff --git a/bench/isNumericValue.bench.ts b/bench/isNumericValue.bench.ts new file mode 100644 index 0000000..a3c1187 --- /dev/null +++ b/bench/isNumericValue.bench.ts @@ -0,0 +1,29 @@ +import { bench } from 'vitest'; + +import { isNumericValue } from '../src/predicate/isNumericValue.js'; + +const simpleNumber = 123.456; +const maxSafeInt = Number.MAX_SAFE_INTEGER; +const bigIntValue = BigInt(Number.MAX_VALUE) + BigInt(Number.MAX_VALUE); +const stringifiedMaxInt = String(maxSafeInt); +const stringifiedBigInt = String(bigIntValue); + +bench('isNumericValue() - simple decimal numbers', () => { + isNumericValue(simpleNumber); +}); + +bench('isNumericValue() - Number.MAX_SAFE_INTEGER', () => { + isNumericValue(maxSafeInt); +}); + +bench('isNumericValue() - bigint', () => { + isNumericValue(bigIntValue); +}); + +bench('isNumericValue() - stringified Number.MAX_SAFE_INTEGER', () => { + isNumericValue(stringifiedMaxInt); +}); + +bench('isNumericValue() - stringified BigInt', () => { + isNumericValue(stringifiedBigInt); +}); diff --git a/package.json b/package.json index bac150c..53fabdf 100644 --- a/package.json +++ b/package.json @@ -66,7 +66,7 @@ "url": "https://github.com/webdeveric/utils/issues" }, "homepage": "https://github.com/webdeveric/utils/#readme", - "packageManager": "pnpm@10.6.4+sha512.da3d715bfd22a9a105e6e8088cfc7826699332ded60c423b14ec613a185f1602206702ff0fe4c438cb15c979081ce4cb02568e364b15174503a63c7a8e2a5f6c", + "packageManager": "pnpm@10.6.5+sha512.cdf928fca20832cd59ec53826492b7dc25dc524d4370b6b4adbf65803d32efaa6c1c88147c0ae4e8d579a6c9eec715757b50d4fa35eea179d868eada4ed043af", "scripts": { "clean": "rimraf ./dist/", "prebuild": "pnpm clean", @@ -87,7 +87,7 @@ "devDependencies": { "@commitlint/config-conventional": "^19.8.0", "@commitlint/types": "^19.8.0", - "@types/node": "^22.13.10", + "@types/node": "^22.13.11", "@vitest/coverage-v8": "^3.0.9", "@webdeveric/eslint-config-ts": "^0.11.0", "@webdeveric/prettier-config": "^0.3.0", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 4a3f19c..d346a6f 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -15,11 +15,11 @@ importers: specifier: ^19.8.0 version: 19.8.0 '@types/node': - specifier: ^22.13.10 - version: 22.13.10 + specifier: ^22.13.11 + version: 22.13.11 '@vitest/coverage-v8': specifier: ^3.0.9 - version: 3.0.9(vitest@3.0.9(@types/node@22.13.10)(jiti@2.4.2)(jsdom@26.0.0)(yaml@2.7.0)) + version: 3.0.9(vitest@3.0.9(@types/node@22.13.11)(jiti@2.4.2)(jsdom@26.0.0)(yaml@2.7.0)) '@webdeveric/eslint-config-ts': specifier: ^0.11.0 version: 0.11.0(eslint@8.57.1)(typescript@5.8.2) @@ -28,7 +28,7 @@ importers: version: 0.3.0(prettier@3.5.3) commitlint: specifier: ^19.8.0 - version: 19.8.0(@types/node@22.13.10)(typescript@5.8.2) + version: 19.8.0(@types/node@22.13.11)(typescript@5.8.2) commitlint-plugin-cspell: specifier: ^0.1.1 version: 0.1.1(@commitlint/lint@19.8.0) @@ -76,7 +76,7 @@ importers: version: 0.8.0 vitest: specifier: ^3.0.9 - version: 3.0.9(@types/node@22.13.10)(jiti@2.4.2)(jsdom@26.0.0)(yaml@2.7.0) + version: 3.0.9(@types/node@22.13.11)(jiti@2.4.2)(jsdom@26.0.0)(yaml@2.7.0) packages: @@ -260,8 +260,8 @@ packages: '@cspell/dict-en-gb@1.1.33': resolution: {integrity: sha512-tKSSUf9BJEV+GJQAYGw5e+ouhEe2ZXE620S7BLKe3ZmpnjlNG9JqlnaBhkIMxKnNFkLY2BP/EARzw31AZnOv4g==} - '@cspell/dict-en_us@4.3.34': - resolution: {integrity: sha512-ewJXNV7Nk5vxbGvHvxYLDGoXN0Lq5sfSgX8SAlcYL+2bZ7r25nNOLHou5hdFlNgvviGTx/SFPlVKjdjVJlblgA==} + '@cspell/dict-en_us@4.3.35': + resolution: {integrity: sha512-HF6QNyPHkxeo/SosaZXRQlnKDUTjIzrGKyqfbw/fPPlPYrXefAZZ40ofheb5HnbUicR7xqV/lsc/HQfqYshGIw==} '@cspell/dict-filetypes@3.0.11': resolution: {integrity: sha512-bBtCHZLo7MiSRUqx5KEiPdGOmXIlDGY+L7SJEtRWZENpAKE+96rT7hj+TUUYWBbCzheqHr0OXZJFEKDgsG/uZg==} @@ -337,8 +337,8 @@ packages: '@cspell/dict-node@5.0.6': resolution: {integrity: sha512-CEbhPCpxGvRNByGolSBTrXXW2rJA4bGqZuTx1KKO85mwR6aadeOmUE7xf/8jiCkXSy+qvr9aJeh+jlfXcsrziQ==} - '@cspell/dict-npm@5.1.30': - resolution: {integrity: sha512-qRMJZFz4FBPECH5rGQN9p2Ld6nfpSaPFQvlG6V2RowWcrJQqF4RFmLUNuRQpvndpSeIUo32yX1hxb7AT45ARCQ==} + '@cspell/dict-npm@5.1.31': + resolution: {integrity: sha512-Oh9nrhgNV4UD1hlbgO3TFQqQRKziwc7qXKoQiC4oqOYIhMs2WL9Ezozku7FY1e7o5XbCIZX9nRH0ymNx/Rwj6w==} '@cspell/dict-php@4.0.14': resolution: {integrity: sha512-7zur8pyncYZglxNmqsRycOZ6inpDoVd4yFfz1pQRe5xaRWMiK3Km4n0/X/1YMWhh3e3Sl/fQg5Axb2hlN68t1g==} @@ -748,11 +748,11 @@ packages: resolution: {integrity: sha512-n57hXtOoHrhwTWdvhVkdJHdhTv0JstjDbDRhJfwIRNfFqmSo1DaK/mD2syoNUoLCyqSjBpGAKOG0BuwF392slw==} engines: {node: '>= 18'} - '@octokit/openapi-types@24.1.0': - resolution: {integrity: sha512-FnYcCZ7MV5dEB+E5ejjhFeg6IFLDcaCCCYcISBeSuTN891Oju1QPnR7k6v/JTwcn2+LjcVhVRcTtUIkVMyzHiw==} + '@octokit/openapi-types@24.2.0': + resolution: {integrity: sha512-9sIH3nSUttelJSXUrmGzl7QUBFul0/mB8HRYl3fOlgHbIWG+WnYDXU3v/2zMtAvuzZ/ed00Ei6on975FhBfzrg==} - '@octokit/plugin-paginate-rest@11.5.0': - resolution: {integrity: sha512-crHS+QeHSiZgWKGgrf+Bpk7DjxSOqtujDHjTTTnzR9OI72Q9+YCWazl95dosDV/1Vyzr64ccW0eLBDHFCkSfzA==} + '@octokit/plugin-paginate-rest@11.6.0': + resolution: {integrity: sha512-n5KPteiF7pWKgBIBJSk8qzoZWcUkza2O6A0za97pMGVrGfPdltxrfmfF5GucHYvHGZD8BdaZmmHGz5cX/3gdpw==} engines: {node: '>= 18'} peerDependencies: '@octokit/core': '>=6' @@ -763,8 +763,8 @@ packages: peerDependencies: '@octokit/core': '>=6' - '@octokit/plugin-throttling@9.5.0': - resolution: {integrity: sha512-qYGE38beMhYvbhs8AvhqDlJSyTwQHZLO08XXFkBUBuIsalX63W56nBFEqb+eaTVlLVhKSXRrnPlrandY77+GVg==} + '@octokit/plugin-throttling@9.6.0': + resolution: {integrity: sha512-zn7m1N3vpJDaVzLqjCRdJ0cRzNiekHEWPi8Ww9xyPNrDt5PStHvVE0eR8wy4RSU8Eg7YO8MHyvn6sv25EGVhhg==} engines: {node: '>= 18'} peerDependencies: '@octokit/core': ^6.1.3 @@ -777,8 +777,8 @@ packages: resolution: {integrity: sha512-dZl0ZHx6gOQGcffgm1/Sf6JfEpmh34v3Af2Uci02vzUYz6qEN6zepoRtmybWXIGXFIK8K9ylE3b+duCWqhArtg==} engines: {node: '>= 18'} - '@octokit/types@13.9.0': - resolution: {integrity: sha512-gRWS+SLIhZV3JUWn9BJt+jd9L4hON2YxRTuZBw20ExPFbqTj7/2kjwzHX1V00wAN4/qJGLz1GF9Kr1DILxTBqg==} + '@octokit/types@13.10.0': + resolution: {integrity: sha512-ifLaO34EbbPj0Xgro4G5lP5asESjwHracYJvVaPIyXMuiuXLlhic3S47cBdTb+jfODkTE5YtGCLt3Ay3+J97sA==} '@pkgjs/parseargs@0.11.0': resolution: {integrity: sha512-+1VkjdD0QBLPodGrJUeqarH8VAIvQODIbwh9XpP5Syisf7YoQgsJKPNFoqqLQlu+VQ/tVSshMR6loPMn8U+dPg==} @@ -981,8 +981,8 @@ packages: '@types/json5@0.0.29': resolution: {integrity: sha512-dRLjCWHYg4oaA77cxO64oO+7JwCwnIzkZPdrrC71jQmQtlhM556pwKo5bUzqvZndkVbeFLIIi+9TC40JNF5hNQ==} - '@types/node@22.13.10': - resolution: {integrity: sha512-I6LPUvlRH+O6VRUqYOcMudhaIdUVWfsjnZavnsraHvpBwaEyMN29ry+0UVJhImYL16xsscu0aske3yA+uPOWfw==} + '@types/node@22.13.11': + resolution: {integrity: sha512-iEUCUJoU0i3VnrCmgoWCXttklWcvoCIx4jzcP22fioIVSdTmjgoEvmAO/QPw6TcS9k5FrNgn4w7q5lGOd1CT5g==} '@types/normalize-package-data@2.4.4': resolution: {integrity: sha512-37i+OaWTh9qeK4LSHPsyRC7NahnGotNuZvjLSgcPzblpHB3rrCJxAOgI5gCdKm7coonsaX1Of0ILiTcnZjbfxA==} @@ -1048,58 +1048,58 @@ packages: '@ungap/structured-clone@1.3.0': resolution: {integrity: sha512-WmoN8qaIAo7WTYWbAZuG8PYEhn5fkz7dZrqTBZ7dtt//lL2Gwms1IcnQ5yHqjDfX8Ft5j4YzDM23f87zBfDe9g==} - '@unrs/rspack-resolver-binding-darwin-arm64@1.2.1': - resolution: {integrity: sha512-xgSjy64typsn/lhQk/uKaS363H7ZeIBlWSh25FJFWXSCeLMHpEZ0umDo5Vzqi5iS26OZ5R1SpQkwiS78GhQRjw==} + '@unrs/rspack-resolver-binding-darwin-arm64@1.2.2': + resolution: {integrity: sha512-i7z0B+C0P8Q63O/5PXJAzeFtA1ttY3OR2VSJgGv18S+PFNwD98xHgAgPOT1H5HIV6jlQP8Avzbp09qxJUdpPNw==} cpu: [arm64] os: [darwin] - '@unrs/rspack-resolver-binding-darwin-x64@1.2.1': - resolution: {integrity: sha512-3maDtW0vehzciEbuLxc2g+0FmDw5LGfCt+yMN1ZDn0lW0ikEBEFp6ul3h2fRphtfuCc7IvBJE9WWTt1UHkS7Nw==} + '@unrs/rspack-resolver-binding-darwin-x64@1.2.2': + resolution: {integrity: sha512-YEdFzPjIbDUCfmehC6eS+AdJYtFWY35YYgWUnqqTM2oe/N58GhNy5yRllxYhxwJ9GcfHoNc6Ubze1yjkNv+9Qg==} cpu: [x64] os: [darwin] - '@unrs/rspack-resolver-binding-freebsd-x64@1.2.1': - resolution: {integrity: sha512-aN6ifws9rNLjK2+6sIU9wvHyjXEf3S5+EZTHRarzd4jfa8i5pA7Mwt28un2DZVrBtIxhWDQvUPVKGI7zSBfVCA==} + '@unrs/rspack-resolver-binding-freebsd-x64@1.2.2': + resolution: {integrity: sha512-TU4ntNXDgPN2giQyyzSnGWf/dVCem5lvwxg0XYvsvz35h5H19WrhTmHgbrULMuypCB3aHe1enYUC9rPLDw45mA==} cpu: [x64] os: [freebsd] - '@unrs/rspack-resolver-binding-linux-arm-gnueabihf@1.2.1': - resolution: {integrity: sha512-tKqu9VQyCO1yEUX6n6jgOHi7SJA9e6lvHczK60gur4VBITxnPmVYiCj2aekrOOIavvvjjuWAL2rqPQuc4g7RHQ==} + '@unrs/rspack-resolver-binding-linux-arm-gnueabihf@1.2.2': + resolution: {integrity: sha512-ik3w4/rU6RujBvNWiDnKdXi1smBhqxEDhccNi/j2rHaMjm0Fk49KkJ6XKsoUnD2kZ5xaMJf9JjailW/okfUPIw==} cpu: [arm] os: [linux] - '@unrs/rspack-resolver-binding-linux-arm64-gnu@1.2.1': - resolution: {integrity: sha512-+xDI0kvwPiCR7334O83TPfaUXSe0UMVi5srQpQxP4+SDVYuONWsbwAC1IXe+yfOwRVGZsUdW9wE0ZiWs4Z+egw==} + '@unrs/rspack-resolver-binding-linux-arm64-gnu@1.2.2': + resolution: {integrity: sha512-fp4Azi8kHz6TX8SFmKfyScZrMLfp++uRm2srpqRjsRZIIBzH74NtSkdEUHImR4G7f7XJ+sVZjCc6KDDK04YEpQ==} cpu: [arm64] os: [linux] - '@unrs/rspack-resolver-binding-linux-arm64-musl@1.2.1': - resolution: {integrity: sha512-fcrVHlw+6UgQliMbI0znFD4ASWKuyY17FdH67ZmyNH62b0hRhhxQuJE0D6N3410m8lKVu4QW4EzFiHxYFUC0cg==} + '@unrs/rspack-resolver-binding-linux-arm64-musl@1.2.2': + resolution: {integrity: sha512-gMiG3DCFioJxdGBzhlL86KcFgt9HGz0iDhw0YVYPsShItpN5pqIkNrI+L/Q/0gfDiGrfcE0X3VANSYIPmqEAlQ==} cpu: [arm64] os: [linux] - '@unrs/rspack-resolver-binding-linux-x64-gnu@1.2.1': - resolution: {integrity: sha512-xISTyUJ2PiAT4x9nlh8FdciDcdKbsatgK9qO7EEsILt9VB7Y1mHYGaszj3ouxfZnaKQ13WwW+dFLGxkZLP/WVg==} + '@unrs/rspack-resolver-binding-linux-x64-gnu@1.2.2': + resolution: {integrity: sha512-n/4n2CxaUF9tcaJxEaZm+lqvaw2gflfWQ1R9I7WQgYkKEKbRKbpG/R3hopYdUmLSRI4xaW1Cy0Bz40eS2Yi4Sw==} cpu: [x64] os: [linux] - '@unrs/rspack-resolver-binding-linux-x64-musl@1.2.1': - resolution: {integrity: sha512-LE8EjE/iPlvSsFbZ6P9c0Jh5/pifAi03UYeXYwOnQqt1molKAPMB0R4kGWOM7dnDYaNgkk1MN9MOTCLsqe97Fw==} + '@unrs/rspack-resolver-binding-linux-x64-musl@1.2.2': + resolution: {integrity: sha512-cHyhAr6rlYYbon1L2Ag449YCj3p6XMfcYTP0AQX+KkQo025d1y/VFtPWvjMhuEsE2lLvtHm7GdJozj6BOMtzVg==} cpu: [x64] os: [linux] - '@unrs/rspack-resolver-binding-wasm32-wasi@1.2.1': - resolution: {integrity: sha512-XERT3B88+G55RgG96May8QvAdgGzHr8qtQ70cIdbuWTpIcA0I76cnxSZ8Qwx33y73jE5N/myX2YKDlFksn4z6w==} + '@unrs/rspack-resolver-binding-wasm32-wasi@1.2.2': + resolution: {integrity: sha512-eogDKuICghDLGc32FtP+WniG38IB1RcGOGz0G3z8406dUdjJvxfHGuGs/dSlM9YEp/v0lEqhJ4mBu6X2nL9pog==} engines: {node: '>=14.0.0'} cpu: [wasm32] - '@unrs/rspack-resolver-binding-win32-arm64-msvc@1.2.1': - resolution: {integrity: sha512-I8OLI6JbmNx2E/SG8MOEuo/d6rNx8dwgL09rcItSMcP82v1oZ8AY8HNA+axxuxEH95nkb6MPJU09p63isDvzrA==} + '@unrs/rspack-resolver-binding-win32-arm64-msvc@1.2.2': + resolution: {integrity: sha512-7sWRJumhpXSi2lccX8aQpfFXHsSVASdWndLv8AmD8nDRA/5PBi8IplQVZNx2mYRx6+Bp91Z00kuVqpXO9NfCTg==} cpu: [arm64] os: [win32] - '@unrs/rspack-resolver-binding-win32-x64-msvc@1.2.1': - resolution: {integrity: sha512-s5WvCljhFqiE3McvaD3lDIsQpmk7gEJRUHy1PRwLPzEB7snq9P2xQeqgzdjGhJQq62jBFz7NDy7NbMkocWr2pw==} + '@unrs/rspack-resolver-binding-win32-x64-msvc@1.2.2': + resolution: {integrity: sha512-hewo/UMGP1a7O6FG/ThcPzSJdm/WwrYDNkdGgWl6M18H6K6MSitklomWpT9MUtT5KGj++QJb06va/14QBC4pvw==} cpu: [x64] os: [win32] @@ -2218,8 +2218,8 @@ packages: resolution: {integrity: sha512-m6FAo/spmsW2Ab2fU35JTYwtOKa2yAwXSwgjSv1TJzh4Mh7mC3lzAOVLBprb72XsTrgkEIsl7YrFNAiDiRhIGg==} engines: {node: '>=12'} - index-to-position@0.1.2: - resolution: {integrity: sha512-MWDKS3AS1bGCHLBA2VLImJz42f7bJh8wQsTGCzI3j519/CASStoDONUBVz2I/VID0MpiX3SGSnbOD2xUalbE5g==} + index-to-position@1.0.0: + resolution: {integrity: sha512-sCO7uaLVhRJ25vz1o8s9IFM3nVS4DkuQnyjMwiQPKvQuBYBDmb8H7zx8ki7nVh4HJQOdVWebyvLE0qt+clruxA==} engines: {node: '>=18'} inflight@1.0.6: @@ -2740,8 +2740,8 @@ packages: mz@2.7.0: resolution: {integrity: sha512-z81GNO7nnYMEhrGh9LeymoE4+Yr0Wn5McHIZMK5cfQCl+NDX08sCZgUc9/6MHni9IWuFLm1Z3HTCXu2z9fN62Q==} - nanoid@3.3.10: - resolution: {integrity: sha512-vSJJTG+t/dIKAUhUDw/dLdZ9s//5OxcHqLaDWWrW4Cdq7o6tdLIczUkMXt2MBNmk6sJRZBZRXVixs7URY1CmIg==} + nanoid@3.3.11: + resolution: {integrity: sha512-N8SpfPUnUp1bK+PMYW8qSWdl9U+wwNWI4QKxOYDy9JAro3WMX7p2OeVRF9v+347pnakNevPmiHhNmZ2HbFA76w==} engines: {node: ^10 || ^12 || ^13.7 || ^14 || >=15.0.1} hasBin: true @@ -3025,8 +3025,8 @@ packages: resolution: {integrity: sha512-ayCKvm/phCGxOkYRSCM82iDwct8/EonSEgCSxWxD7ve6jHggsFl4fZVQBPRNgQoKiuV/odhFrGzQXZwbifC8Rg==} engines: {node: '>=8'} - parse-json@8.1.0: - resolution: {integrity: sha512-rum1bPifK5SSar35Z6EKZuYPJx85pkNaFrxBK3mwdfSJ1/WKbYrjoW/zTPSjRRamfmVX1ACBIdFAO0VRErW/EA==} + parse-json@8.2.0: + resolution: {integrity: sha512-eONBZy4hm2AgxjNFd8a4nyDJnzUAH0g34xSQAwWEVGCjdZ4ZL7dKZBfq267GWP/JaS9zW62Xs2FeAdDvpHHJGQ==} engines: {node: '>=18'} parse-ms@4.0.0: @@ -3275,8 +3275,8 @@ packages: rrweb-cssom@0.8.0: resolution: {integrity: sha512-guoltQEx+9aMf2gDZ0s62EcV8lsXR+0w8915TC3ITdn2YueuNjdAYh/levpU9nFaoChh9RUS5ZdQMrKfVEN9tw==} - rspack-resolver@1.2.1: - resolution: {integrity: sha512-yTaWGUvHOjcoyFMdVTdYt2nq2Hu8sw6ia3X9szloXFJlWLQZnQ9g/4TPhL3Bb3qN58Mkye8mFG7MCaKhya7fOw==} + rspack-resolver@1.2.2: + resolution: {integrity: sha512-Fwc19jMBA3g+fxDJH2B4WxwZjE0VaaOL7OX/A4Wn5Zv7bOD/vyPZhzXfaO73Xc2GAlfi96g5fGUa378WbIGfFw==} run-parallel@1.2.0: resolution: {integrity: sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==} @@ -4023,11 +4023,11 @@ snapshots: '@colors/colors@1.5.0': optional: true - '@commitlint/cli@19.8.0(@types/node@22.13.10)(typescript@5.8.2)': + '@commitlint/cli@19.8.0(@types/node@22.13.11)(typescript@5.8.2)': dependencies: '@commitlint/format': 19.8.0 '@commitlint/lint': 19.8.0 - '@commitlint/load': 19.8.0(@types/node@22.13.10)(typescript@5.8.2) + '@commitlint/load': 19.8.0(@types/node@22.13.11)(typescript@5.8.2) '@commitlint/read': 19.8.0 '@commitlint/types': 19.8.0 tinyexec: 0.3.2 @@ -4074,7 +4074,7 @@ snapshots: '@commitlint/rules': 19.8.0 '@commitlint/types': 19.8.0 - '@commitlint/load@19.8.0(@types/node@22.13.10)(typescript@5.8.2)': + '@commitlint/load@19.8.0(@types/node@22.13.11)(typescript@5.8.2)': dependencies: '@commitlint/config-validator': 19.8.0 '@commitlint/execute-rule': 19.8.0 @@ -4082,7 +4082,7 @@ snapshots: '@commitlint/types': 19.8.0 chalk: 5.4.1 cosmiconfig: 9.0.0(typescript@5.8.2) - cosmiconfig-typescript-loader: 6.1.0(@types/node@22.13.10)(cosmiconfig@9.0.0(typescript@5.8.2))(typescript@5.8.2) + cosmiconfig-typescript-loader: 6.1.0(@types/node@22.13.11)(cosmiconfig@9.0.0(typescript@5.8.2))(typescript@5.8.2) lodash.isplainobject: 4.0.6 lodash.merge: 4.6.2 lodash.uniq: 4.5.0 @@ -4152,7 +4152,7 @@ snapshots: '@cspell/dict-elixir': 4.0.7 '@cspell/dict-en-common-misspellings': 2.0.10 '@cspell/dict-en-gb': 1.1.33 - '@cspell/dict-en_us': 4.3.34 + '@cspell/dict-en_us': 4.3.35 '@cspell/dict-filetypes': 3.0.11 '@cspell/dict-flutter': 1.1.0 '@cspell/dict-fonts': 4.0.4 @@ -4176,7 +4176,7 @@ snapshots: '@cspell/dict-markdown': 2.0.9(@cspell/dict-css@4.0.17)(@cspell/dict-html-symbol-entities@4.0.3)(@cspell/dict-html@4.0.11)(@cspell/dict-typescript@3.2.0) '@cspell/dict-monkeyc': 1.0.10 '@cspell/dict-node': 5.0.6 - '@cspell/dict-npm': 5.1.30 + '@cspell/dict-npm': 5.1.31 '@cspell/dict-php': 4.0.14 '@cspell/dict-powershell': 5.0.14 '@cspell/dict-public-licenses': 2.0.13 @@ -4244,7 +4244,7 @@ snapshots: '@cspell/dict-en-gb@1.1.33': {} - '@cspell/dict-en_us@4.3.34': {} + '@cspell/dict-en_us@4.3.35': {} '@cspell/dict-filetypes@3.0.11': {} @@ -4297,7 +4297,7 @@ snapshots: '@cspell/dict-node@5.0.6': {} - '@cspell/dict-npm@5.1.30': {} + '@cspell/dict-npm@5.1.31': {} '@cspell/dict-php@4.0.14': {} @@ -4675,56 +4675,56 @@ snapshots: '@octokit/graphql': 8.2.1 '@octokit/request': 9.2.2 '@octokit/request-error': 6.1.7 - '@octokit/types': 13.9.0 + '@octokit/types': 13.10.0 before-after-hook: 3.0.2 universal-user-agent: 7.0.2 '@octokit/endpoint@10.1.3': dependencies: - '@octokit/types': 13.9.0 + '@octokit/types': 13.10.0 universal-user-agent: 7.0.2 '@octokit/graphql@8.2.1': dependencies: '@octokit/request': 9.2.2 - '@octokit/types': 13.9.0 + '@octokit/types': 13.10.0 universal-user-agent: 7.0.2 - '@octokit/openapi-types@24.1.0': {} + '@octokit/openapi-types@24.2.0': {} - '@octokit/plugin-paginate-rest@11.5.0(@octokit/core@6.1.4)': + '@octokit/plugin-paginate-rest@11.6.0(@octokit/core@6.1.4)': dependencies: '@octokit/core': 6.1.4 - '@octokit/types': 13.9.0 + '@octokit/types': 13.10.0 '@octokit/plugin-retry@7.2.0(@octokit/core@6.1.4)': dependencies: '@octokit/core': 6.1.4 '@octokit/request-error': 6.1.7 - '@octokit/types': 13.9.0 + '@octokit/types': 13.10.0 bottleneck: 2.19.5 - '@octokit/plugin-throttling@9.5.0(@octokit/core@6.1.4)': + '@octokit/plugin-throttling@9.6.0(@octokit/core@6.1.4)': dependencies: '@octokit/core': 6.1.4 - '@octokit/types': 13.9.0 + '@octokit/types': 13.10.0 bottleneck: 2.19.5 '@octokit/request-error@6.1.7': dependencies: - '@octokit/types': 13.9.0 + '@octokit/types': 13.10.0 '@octokit/request@9.2.2': dependencies: '@octokit/endpoint': 10.1.3 '@octokit/request-error': 6.1.7 - '@octokit/types': 13.9.0 + '@octokit/types': 13.10.0 fast-content-type-parse: 2.0.1 universal-user-agent: 7.0.2 - '@octokit/types@13.9.0': + '@octokit/types@13.10.0': dependencies: - '@octokit/openapi-types': 24.1.0 + '@octokit/openapi-types': 24.2.0 '@pkgjs/parseargs@0.11.0': optional: true @@ -4821,9 +4821,9 @@ snapshots: '@semantic-release/github@11.0.1(semantic-release@24.2.3(typescript@5.8.2))': dependencies: '@octokit/core': 6.1.4 - '@octokit/plugin-paginate-rest': 11.5.0(@octokit/core@6.1.4) + '@octokit/plugin-paginate-rest': 11.6.0(@octokit/core@6.1.4) '@octokit/plugin-retry': 7.2.0(@octokit/core@6.1.4) - '@octokit/plugin-throttling': 9.5.0(@octokit/core@6.1.4) + '@octokit/plugin-throttling': 9.6.0(@octokit/core@6.1.4) '@semantic-release/error': 4.0.0 aggregate-error: 5.0.0 debug: 4.4.0 @@ -4925,13 +4925,13 @@ snapshots: '@types/conventional-commits-parser@5.0.1': dependencies: - '@types/node': 22.13.10 + '@types/node': 22.13.11 '@types/estree@1.0.6': {} '@types/json5@0.0.29': {} - '@types/node@22.13.10': + '@types/node@22.13.11': dependencies: undici-types: 6.20.0 @@ -5020,42 +5020,42 @@ snapshots: '@ungap/structured-clone@1.3.0': {} - '@unrs/rspack-resolver-binding-darwin-arm64@1.2.1': + '@unrs/rspack-resolver-binding-darwin-arm64@1.2.2': optional: true - '@unrs/rspack-resolver-binding-darwin-x64@1.2.1': + '@unrs/rspack-resolver-binding-darwin-x64@1.2.2': optional: true - '@unrs/rspack-resolver-binding-freebsd-x64@1.2.1': + '@unrs/rspack-resolver-binding-freebsd-x64@1.2.2': optional: true - '@unrs/rspack-resolver-binding-linux-arm-gnueabihf@1.2.1': + '@unrs/rspack-resolver-binding-linux-arm-gnueabihf@1.2.2': optional: true - '@unrs/rspack-resolver-binding-linux-arm64-gnu@1.2.1': + '@unrs/rspack-resolver-binding-linux-arm64-gnu@1.2.2': optional: true - '@unrs/rspack-resolver-binding-linux-arm64-musl@1.2.1': + '@unrs/rspack-resolver-binding-linux-arm64-musl@1.2.2': optional: true - '@unrs/rspack-resolver-binding-linux-x64-gnu@1.2.1': + '@unrs/rspack-resolver-binding-linux-x64-gnu@1.2.2': optional: true - '@unrs/rspack-resolver-binding-linux-x64-musl@1.2.1': + '@unrs/rspack-resolver-binding-linux-x64-musl@1.2.2': optional: true - '@unrs/rspack-resolver-binding-wasm32-wasi@1.2.1': + '@unrs/rspack-resolver-binding-wasm32-wasi@1.2.2': dependencies: '@napi-rs/wasm-runtime': 0.2.7 optional: true - '@unrs/rspack-resolver-binding-win32-arm64-msvc@1.2.1': + '@unrs/rspack-resolver-binding-win32-arm64-msvc@1.2.2': optional: true - '@unrs/rspack-resolver-binding-win32-x64-msvc@1.2.1': + '@unrs/rspack-resolver-binding-win32-x64-msvc@1.2.2': optional: true - '@vitest/coverage-v8@3.0.9(vitest@3.0.9(@types/node@22.13.10)(jiti@2.4.2)(jsdom@26.0.0)(yaml@2.7.0))': + '@vitest/coverage-v8@3.0.9(vitest@3.0.9(@types/node@22.13.11)(jiti@2.4.2)(jsdom@26.0.0)(yaml@2.7.0))': dependencies: '@ampproject/remapping': 2.3.0 '@bcoe/v8-coverage': 1.0.2 @@ -5069,7 +5069,7 @@ snapshots: std-env: 3.8.1 test-exclude: 7.0.1 tinyrainbow: 2.0.0 - vitest: 3.0.9(@types/node@22.13.10)(jiti@2.4.2)(jsdom@26.0.0)(yaml@2.7.0) + vitest: 3.0.9(@types/node@22.13.11)(jiti@2.4.2)(jsdom@26.0.0)(yaml@2.7.0) transitivePeerDependencies: - supports-color @@ -5080,13 +5080,13 @@ snapshots: chai: 5.2.0 tinyrainbow: 2.0.0 - '@vitest/mocker@3.0.9(vite@6.2.2(@types/node@22.13.10)(jiti@2.4.2)(yaml@2.7.0))': + '@vitest/mocker@3.0.9(vite@6.2.2(@types/node@22.13.11)(jiti@2.4.2)(yaml@2.7.0))': dependencies: '@vitest/spy': 3.0.9 estree-walker: 3.0.3 magic-string: 0.30.17 optionalDependencies: - vite: 6.2.2(@types/node@22.13.10)(jiti@2.4.2)(yaml@2.7.0) + vite: 6.2.2(@types/node@22.13.11)(jiti@2.4.2)(yaml@2.7.0) '@vitest/pretty-format@3.0.9': dependencies: @@ -5432,9 +5432,9 @@ snapshots: '@commitlint/types': 19.8.0 cspell-lib: 8.17.5 - commitlint@19.8.0(@types/node@22.13.10)(typescript@5.8.2): + commitlint@19.8.0(@types/node@22.13.11)(typescript@5.8.2): dependencies: - '@commitlint/cli': 19.8.0(@types/node@22.13.10)(typescript@5.8.2) + '@commitlint/cli': 19.8.0(@types/node@22.13.11)(typescript@5.8.2) '@commitlint/types': 19.8.0 transitivePeerDependencies: - '@types/node' @@ -5494,9 +5494,9 @@ snapshots: core-util-is@1.0.3: {} - cosmiconfig-typescript-loader@6.1.0(@types/node@22.13.10)(cosmiconfig@9.0.0(typescript@5.8.2))(typescript@5.8.2): + cosmiconfig-typescript-loader@6.1.0(@types/node@22.13.11)(cosmiconfig@9.0.0(typescript@5.8.2))(typescript@5.8.2): dependencies: - '@types/node': 22.13.10 + '@types/node': 22.13.11 cosmiconfig: 9.0.0(typescript@5.8.2) jiti: 2.4.2 typescript: 5.8.2 @@ -5867,7 +5867,7 @@ snapshots: eslint: 8.57.1 get-tsconfig: 4.10.0 is-bun-module: 1.3.0 - rspack-resolver: 1.2.1 + rspack-resolver: 1.2.2 stable-hash: 0.0.5 tinyglobby: 0.2.12 optionalDependencies: @@ -6401,7 +6401,7 @@ snapshots: indent-string@5.0.0: {} - index-to-position@0.1.2: {} + index-to-position@1.0.0: {} inflight@1.0.6: dependencies: @@ -6925,7 +6925,7 @@ snapshots: object-assign: 4.1.1 thenify-all: 1.6.0 - nanoid@3.3.10: {} + nanoid@3.3.11: {} natural-compare@1.4.0: {} @@ -7199,10 +7199,10 @@ snapshots: json-parse-even-better-errors: 2.3.1 lines-and-columns: 1.2.4 - parse-json@8.1.0: + parse-json@8.2.0: dependencies: '@babel/code-frame': 7.26.2 - index-to-position: 0.1.2 + index-to-position: 1.0.0 type-fest: 4.37.0 parse-ms@4.0.0: {} @@ -7275,7 +7275,7 @@ snapshots: postcss@8.5.3: dependencies: - nanoid: 3.3.10 + nanoid: 3.3.11 picocolors: 1.1.1 source-map-js: 1.2.1 @@ -7332,7 +7332,7 @@ snapshots: dependencies: '@types/normalize-package-data': 2.4.4 normalize-package-data: 6.0.2 - parse-json: 8.1.0 + parse-json: 8.2.0 type-fest: 4.37.0 unicorn-magic: 0.1.0 @@ -7439,19 +7439,19 @@ snapshots: rrweb-cssom@0.8.0: {} - rspack-resolver@1.2.1: + rspack-resolver@1.2.2: optionalDependencies: - '@unrs/rspack-resolver-binding-darwin-arm64': 1.2.1 - '@unrs/rspack-resolver-binding-darwin-x64': 1.2.1 - '@unrs/rspack-resolver-binding-freebsd-x64': 1.2.1 - '@unrs/rspack-resolver-binding-linux-arm-gnueabihf': 1.2.1 - '@unrs/rspack-resolver-binding-linux-arm64-gnu': 1.2.1 - '@unrs/rspack-resolver-binding-linux-arm64-musl': 1.2.1 - '@unrs/rspack-resolver-binding-linux-x64-gnu': 1.2.1 - '@unrs/rspack-resolver-binding-linux-x64-musl': 1.2.1 - '@unrs/rspack-resolver-binding-wasm32-wasi': 1.2.1 - '@unrs/rspack-resolver-binding-win32-arm64-msvc': 1.2.1 - '@unrs/rspack-resolver-binding-win32-x64-msvc': 1.2.1 + '@unrs/rspack-resolver-binding-darwin-arm64': 1.2.2 + '@unrs/rspack-resolver-binding-darwin-x64': 1.2.2 + '@unrs/rspack-resolver-binding-freebsd-x64': 1.2.2 + '@unrs/rspack-resolver-binding-linux-arm-gnueabihf': 1.2.2 + '@unrs/rspack-resolver-binding-linux-arm64-gnu': 1.2.2 + '@unrs/rspack-resolver-binding-linux-arm64-musl': 1.2.2 + '@unrs/rspack-resolver-binding-linux-x64-gnu': 1.2.2 + '@unrs/rspack-resolver-binding-linux-x64-musl': 1.2.2 + '@unrs/rspack-resolver-binding-wasm32-wasi': 1.2.2 + '@unrs/rspack-resolver-binding-win32-arm64-msvc': 1.2.2 + '@unrs/rspack-resolver-binding-win32-x64-msvc': 1.2.2 run-parallel@1.2.0: dependencies: @@ -7988,13 +7988,13 @@ snapshots: transitivePeerDependencies: - supports-color - vite-node@3.0.9(@types/node@22.13.10)(jiti@2.4.2)(yaml@2.7.0): + vite-node@3.0.9(@types/node@22.13.11)(jiti@2.4.2)(yaml@2.7.0): dependencies: cac: 6.7.14 debug: 4.4.0 es-module-lexer: 1.6.0 pathe: 2.0.3 - vite: 6.2.2(@types/node@22.13.10)(jiti@2.4.2)(yaml@2.7.0) + vite: 6.2.2(@types/node@22.13.11)(jiti@2.4.2)(yaml@2.7.0) transitivePeerDependencies: - '@types/node' - jiti @@ -8009,21 +8009,21 @@ snapshots: - tsx - yaml - vite@6.2.2(@types/node@22.13.10)(jiti@2.4.2)(yaml@2.7.0): + vite@6.2.2(@types/node@22.13.11)(jiti@2.4.2)(yaml@2.7.0): dependencies: esbuild: 0.25.1 postcss: 8.5.3 rollup: 4.36.0 optionalDependencies: - '@types/node': 22.13.10 + '@types/node': 22.13.11 fsevents: 2.3.3 jiti: 2.4.2 yaml: 2.7.0 - vitest@3.0.9(@types/node@22.13.10)(jiti@2.4.2)(jsdom@26.0.0)(yaml@2.7.0): + vitest@3.0.9(@types/node@22.13.11)(jiti@2.4.2)(jsdom@26.0.0)(yaml@2.7.0): dependencies: '@vitest/expect': 3.0.9 - '@vitest/mocker': 3.0.9(vite@6.2.2(@types/node@22.13.10)(jiti@2.4.2)(yaml@2.7.0)) + '@vitest/mocker': 3.0.9(vite@6.2.2(@types/node@22.13.11)(jiti@2.4.2)(yaml@2.7.0)) '@vitest/pretty-format': 3.0.9 '@vitest/runner': 3.0.9 '@vitest/snapshot': 3.0.9 @@ -8039,11 +8039,11 @@ snapshots: tinyexec: 0.3.2 tinypool: 1.0.2 tinyrainbow: 2.0.0 - vite: 6.2.2(@types/node@22.13.10)(jiti@2.4.2)(yaml@2.7.0) - vite-node: 3.0.9(@types/node@22.13.10)(jiti@2.4.2)(yaml@2.7.0) + vite: 6.2.2(@types/node@22.13.11)(jiti@2.4.2)(yaml@2.7.0) + vite-node: 3.0.9(@types/node@22.13.11)(jiti@2.4.2)(yaml@2.7.0) why-is-node-running: 2.3.0 optionalDependencies: - '@types/node': 22.13.10 + '@types/node': 22.13.11 jsdom: 26.0.0 transitivePeerDependencies: - jiti diff --git a/src/predicate/factory/shape.ts b/src/predicate/factory/shape.ts index 0dbba8d..4e21d15 100644 --- a/src/predicate/factory/shape.ts +++ b/src/predicate/factory/shape.ts @@ -37,8 +37,10 @@ export type InferTypeFromShape> = Pretty export const shape = = ObjectShapeRecord>( objectShape: Shape, ): TypePredicateFn> => { - const entries: [key: string, predicate: TypePredicateFn][] = Object.entries(objectShape).map( - ([key, value]) => { + const entries: [key: string | symbol, predicate: TypePredicateFn][] = Reflect.ownKeys(objectShape).map( + (key) => { + const value = Reflect.get(objectShape, key); + const predicate = typeof value === 'function' ? (value as TypePredicateFn) @@ -53,5 +55,5 @@ export const shape = => - isAnyObject(input) && entries.every(([key, predicate]) => predicate(input[key])); + isAnyObject(input) && entries.every(([key, predicate]) => predicate(Reflect.get(input, key))); }; diff --git a/src/predicate/index.ts b/src/predicate/index.ts index d95cb4e..bb225e4 100644 --- a/src/predicate/index.ts +++ b/src/predicate/index.ts @@ -1,14 +1,18 @@ +export * from './isAny.js'; export * from './isAnyObject.js'; export * from './isAnyObjectWith.js'; export * from './isAnyObjectWithOwn.js'; +export * from './isArray.js'; export * from './isAsyncIterable.js'; export * from './isBigInt.js'; export * from './isBigIntArray.js'; +export * from './isBinaryNumberString.js'; export * from './isBoolean.js'; export * from './isBooleanArray.js'; export * from './isDigitsString.js'; export * from './isFiniteNumber.js'; export * from './isFunction.js'; +export * from './isHexNumberString.js'; export * from './isInt32.js'; export * from './isInteger.js'; export * from './isIntString.js'; @@ -25,11 +29,14 @@ export * from './isNumericValue.js'; export * from './isNumericValueArray.js'; export * from './isObject.js'; export * from './isObjectWith.js'; +export * from './isOctalNumberString.js'; +export * from './isOptionalArray.js'; export * from './isOptionalBigInt.js'; export * from './isOptionalBoolean.js'; export * from './isOptionalISODateString.js'; export * from './isOptionalNull.js'; export * from './isOptionalNumber.js'; +export * from './isOptionalObject.js'; export * from './isOptionalString.js'; export * from './isOptionalSymbol.js'; export * from './isPositiveFiniteNumber.js'; @@ -48,3 +55,4 @@ export * from './isSymbol.js'; export * from './isSymbolArray.js'; export * from './isUndefined.js'; export * from './isUndefinedArray.js'; +export * from './isUnknown.js'; diff --git a/src/predicate/isAny.ts b/src/predicate/isAny.ts new file mode 100644 index 0000000..3158f88 --- /dev/null +++ b/src/predicate/isAny.ts @@ -0,0 +1,2 @@ +/* eslint-disable @typescript-eslint/no-explicit-any */ +export const isAny = (_input: unknown): _input is any => true; diff --git a/src/predicate/isAnyObject.ts b/src/predicate/isAnyObject.ts index 3ddf6c0..81fb8c6 100644 --- a/src/predicate/isAnyObject.ts +++ b/src/predicate/isAnyObject.ts @@ -1,6 +1,4 @@ -import type { UnknownRecord } from '../types/records.js'; - /** * Determine if `input` is any non-null object. */ -export const isAnyObject = (input: T): input is T & UnknownRecord => input !== null && typeof input === 'object'; +export const isAnyObject = (input: unknown): input is object => input !== null && typeof input === 'object'; diff --git a/src/predicate/isArray.ts b/src/predicate/isArray.ts new file mode 100644 index 0000000..a3a20ed --- /dev/null +++ b/src/predicate/isArray.ts @@ -0,0 +1,4 @@ +/** + * Determine if `input` is an array. + */ +export const isArray = (input: unknown): input is unknown[] => Array.isArray(input); diff --git a/src/predicate/isAsyncIterable.ts b/src/predicate/isAsyncIterable.ts index 24ba37e..54c9485 100644 --- a/src/predicate/isAsyncIterable.ts +++ b/src/predicate/isAsyncIterable.ts @@ -1,4 +1,8 @@ -export const isAsyncIterable = (input: unknown): input is AsyncIterable => { +/** + * @see https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Iteration_protocols#the_async_iterator_and_async_iterable_protocols + * @see https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Symbol/asyncIterator + */ +export const isAsyncIterable = (input: unknown): input is AsyncIterable => { return ( input !== null && typeof input === 'object' && diff --git a/src/predicate/isBinaryNumberString.ts b/src/predicate/isBinaryNumberString.ts new file mode 100644 index 0000000..706b650 --- /dev/null +++ b/src/predicate/isBinaryNumberString.ts @@ -0,0 +1,8 @@ +import { matching } from './factory/matching.js'; + +import type { BinaryNumberString } from '../types/numbers.js'; + +/** + * @see https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Numbers_and_strings#binary_numbers + */ +export const isBinaryNumberString = matching(/^0b[01]+$/i); diff --git a/src/predicate/isHexNumberString.ts b/src/predicate/isHexNumberString.ts new file mode 100644 index 0000000..3621195 --- /dev/null +++ b/src/predicate/isHexNumberString.ts @@ -0,0 +1,8 @@ +import { matching } from './factory/matching.js'; + +import type { HexNumberString } from '../types/numbers.js'; + +/** + * @see https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Numbers_and_strings#hexadecimal_numbers + */ +export const isHexNumberString = matching(/^0x[0-9a-f]+$/i); diff --git a/src/predicate/isNumericValue.ts b/src/predicate/isNumericValue.ts index 5625001..56b867d 100644 --- a/src/predicate/isNumericValue.ts +++ b/src/predicate/isNumericValue.ts @@ -3,4 +3,4 @@ import { isNumericString } from './isNumericString.js'; import type { NumericValue } from '../types/common.js'; export const isNumericValue = (input: unknown): input is NumericValue => - typeof input === 'number' || typeof input === 'bigint' || isNumericString(input); + (typeof input === 'number' && !Number.isNaN(input)) || typeof input === 'bigint' || isNumericString(input); diff --git a/src/predicate/isObject.ts b/src/predicate/isObject.ts index d403661..fe47df3 100644 --- a/src/predicate/isObject.ts +++ b/src/predicate/isObject.ts @@ -1,7 +1,5 @@ -import type { UnknownRecord } from '../types/records.js'; - /** * Determine if `input` is a non-null object and not an array. */ -export const isObject = (input: T): input is T & UnknownRecord => +export const isObject = (input: unknown): input is object => input !== null && typeof input === 'object' && !Array.isArray(input); diff --git a/src/predicate/isOctalNumberString.ts b/src/predicate/isOctalNumberString.ts new file mode 100644 index 0000000..11d1e2a --- /dev/null +++ b/src/predicate/isOctalNumberString.ts @@ -0,0 +1,8 @@ +import { matching } from './factory/matching.js'; + +import type { OctalNumberString } from '../types/numbers.js'; + +/** + * @see https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Numbers_and_strings#octal_numbers + */ +export const isOctalNumberString = matching(/^0o[0-7]+$/i); diff --git a/src/predicate/isOptionalArray.ts b/src/predicate/isOptionalArray.ts new file mode 100644 index 0000000..a19350a --- /dev/null +++ b/src/predicate/isOptionalArray.ts @@ -0,0 +1,4 @@ +import { optional } from './factory/optional.js'; +import { isArray } from './isArray.js'; + +export const isOptionalArray = optional(isArray); diff --git a/src/predicate/isOptionalObject.ts b/src/predicate/isOptionalObject.ts new file mode 100644 index 0000000..091eb77 --- /dev/null +++ b/src/predicate/isOptionalObject.ts @@ -0,0 +1,4 @@ +import { optional } from './factory/optional.js'; +import { isObject } from './isObject.js'; + +export const isOptionalObject = optional(isObject); diff --git a/src/predicate/isUnknown.ts b/src/predicate/isUnknown.ts new file mode 100644 index 0000000..4eafb1e --- /dev/null +++ b/src/predicate/isUnknown.ts @@ -0,0 +1 @@ +export const isUnknown = (_input: unknown): _input is unknown => true; diff --git a/src/types/common.ts b/src/types/common.ts index 4886aff..433cbe8 100644 --- a/src/types/common.ts +++ b/src/types/common.ts @@ -4,7 +4,12 @@ import type { NotPromise } from './utils.js'; export type Primitive = string | number | boolean | bigint | symbol | undefined | null; -export type NumericString = `${T}` extends `${number}` ? `${T}` : never; +/** + * This is any number or bigint that has been stringified. + */ +export type NumericString = `${T}` extends `${number | bigint}` + ? `${T}` + : never; export type NumericValue = number | bigint | NumericString; diff --git a/src/types/numbers.ts b/src/types/numbers.ts index adfa774..df33954 100644 --- a/src/types/numbers.ts +++ b/src/types/numbers.ts @@ -1,5 +1,32 @@ import type { Branded } from './branded.js'; +export type Digit = 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9; + +/** + * @see https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Numbers_and_strings#decimal_numbers + */ +export type DecimalNumberString = `${'-' | '+' | ''}${Digit}${number}`; + +/** + * @see https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Numbers_and_strings#hexadecimal_numbers + */ +export type HexNumberString = `0${'x' | 'X'}${string}`; + +/** + * @see https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Numbers_and_strings#octal_numbers + */ +export type OctalNumberString = `0${'o' | 'O'}${number}`; + +/** + * @see https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Numbers_and_strings#binary_numbers + */ +export type BinaryNumberString = `0${'b' | 'B'}${number}`; + +/** + * @see https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Numbers_and_strings#exponentiation + */ +export type ExponentiationString = `${number}${'e' | 'E'}${number}`; + export type Int32 = Branded; export type NumberRange = diff --git a/src/types/strings.ts b/src/types/strings.ts index 7a52590..0c2d44b 100644 --- a/src/types/strings.ts +++ b/src/types/strings.ts @@ -1,3 +1,5 @@ +import type { Digit } from './numbers.js'; + export type EmptyString = ''; export type Space = ' '; @@ -30,7 +32,7 @@ export type AlphaCharacter = | 'y' | 'z'; -export type NumericCharacter = '0' | '1' | '2' | '3' | '4' | '5' | '6' | '7' | '8' | '9'; +export type NumericCharacter = `${Digit}`; export type AlphanumericCharacter = AlphaCharacter | Capitalize | NumericCharacter; diff --git a/test/predicate/factory/shape.test.ts b/test/predicate/factory/shape.test.ts index 876a2f6..a3dc5d0 100644 --- a/test/predicate/factory/shape.test.ts +++ b/test/predicate/factory/shape.test.ts @@ -1,7 +1,8 @@ -import { describe, expectTypeOf, it } from 'vitest'; +import { describe, expect, it } from 'vitest'; +import { optional } from '../../../src/predicate/factory/optional.js'; import { range } from '../../../src/predicate/factory/range.js'; -import { shape, type InferTypeFromShape, type ObjectShapeRecord } from '../../../src/predicate/factory/shape.js'; +import { shape, type ObjectShapeRecord } from '../../../src/predicate/factory/shape.js'; import { isNumber } from '../../../src/predicate/isNumber.js'; import { isOptionalString } from '../../../src/predicate/isOptionalString.js'; import { isString } from '../../../src/predicate/isString.js'; @@ -12,79 +13,72 @@ describe('shape()', () => { User = 'user', } + const valueSymbol = Symbol.for('value'); + type User = { name: string; role: Role; value: number; age?: number; - contact: { + [valueSymbol]: number; + contact?: { email: string; phone?: string; }; tuple: [key: string, value: number]; }; - const user: unknown = { - name: 'Test', - role: Role.User, - value: 50, - age: 100, - contact: { - email: 'test@example.com', - phone: undefined, - }, - tuple: ['PI', Math.PI], - } satisfies User; - type UserShape = ObjectShapeRecord; const userShape = { name: /^Test$/, role: Role.User, value: range(0, 100), + [valueSymbol]: range(0, 100_000), age: isNumber, - contact: { - email: isString, - phone: isOptionalString, - }, + contact: optional( + shape({ + email: isString, + phone: isOptionalString, + }), + ), tuple: ['PI', Math.PI], } satisfies UserShape; - type InferredUserType = InferTypeFromShape; - type InferredUserTypeConst = InferTypeFromShape; + const fn = shape(userShape); it('Returns a type predicate function', () => { - const fn = shape(userShape); - - expectTypeOf(fn).toBeFunction(); - expectTypeOf(fn).parameter(0).toMatchTypeOf(); + expect(fn).instanceOf(Function); }); - describe('Type parameters', () => { - it('Infers type parameter values', () => { - const fn = shape(userShape); - - if (fn(user)) { - expectTypeOf(user).toMatchTypeOf(); - } - }); - - it('Can provide the Type to constrain the Shape', () => { - const fn = shape(userShape); - - if (fn(user)) { - expectTypeOf(user).toMatchTypeOf(); - } - }); + it('Checks string and symbol properties', () => { + expect(fn({})).toBeFalsy(); - it('Can provide the Type and Shape parameters', () => { - const fn = shape<{ name: string }, { name: string | RegExp }>({ - name: 'test', - }); + expect( + fn({ + name: 'Test', + role: Role.User, + value: 100, + [valueSymbol]: 100_000, + age: 100, + contact: undefined, + tuple: ['PI', Math.PI], + }), + ).toBeTruthy(); - if (fn(user)) { - expectTypeOf(user).toMatchTypeOf<{ name: string }>(); - } - }); + expect( + fn({ + name: 'Test', + role: Role.User, + value: 100, + [valueSymbol]: 100_000, + age: 100, + contact: { + email: 'me@example.com', + phone: '555-1234', + }, + tuple: ['PI', Math.PI], + }), + ).toBeTruthy(); }); }); diff --git a/test/predicate/isAny.test.ts b/test/predicate/isAny.test.ts new file mode 100644 index 0000000..9a34fd6 --- /dev/null +++ b/test/predicate/isAny.test.ts @@ -0,0 +1,9 @@ +import { describe, it, expect } from 'vitest'; + +import { isAny } from '../../src/predicate/isAny.js'; + +describe('isAny()', () => { + it.each([1, 'a', true, null, {}, undefined])('Always Returns true: %j', (input) => { + expect(isAny(input)).toBeTruthy(); + }); +}); diff --git a/test/predicate/isArray.test.ts b/test/predicate/isArray.test.ts new file mode 100644 index 0000000..53bacac --- /dev/null +++ b/test/predicate/isArray.test.ts @@ -0,0 +1,11 @@ +import { describe, it, expect } from 'vitest'; + +import { isArray } from '../../src/predicate/isArray.js'; + +describe('isArray()', () => { + it('Returns true for valid inputs', () => { + expect(isArray([1, 2, 3])).toBeTruthy(); + expect(isArray(null)).toBeFalsy(); + expect(isArray({})).toBeFalsy(); + }); +}); diff --git a/test/predicate/isBinaryNumberString.test.ts b/test/predicate/isBinaryNumberString.test.ts new file mode 100644 index 0000000..c0d6f38 --- /dev/null +++ b/test/predicate/isBinaryNumberString.test.ts @@ -0,0 +1,17 @@ +import { describe, it, expect } from 'vitest'; + +import { isBinaryNumberString } from '../../src/predicate/isBinaryNumberString.js'; + +describe('isBinaryNumberString()', () => { + it('Returns true for valid input', () => { + expect(isBinaryNumberString('0b10')).toBeTruthy(); + expect(isBinaryNumberString('0B10')).toBeTruthy(); + }); + + it('Returns false for invalid input', () => { + expect(isBinaryNumberString('0b012')).toBeFalsy(); + expect(isBinaryNumberString(null)).toBeFalsy(); + expect(isBinaryNumberString(undefined)).toBeFalsy(); + expect(isBinaryNumberString(Number.MAX_SAFE_INTEGER)).toBeFalsy(); + }); +}); diff --git a/test/predicate/isHexNumberString.test.ts b/test/predicate/isHexNumberString.test.ts new file mode 100644 index 0000000..28e0459 --- /dev/null +++ b/test/predicate/isHexNumberString.test.ts @@ -0,0 +1,16 @@ +import { describe, it, expect } from 'vitest'; + +import { isHexNumberString } from '../../src/predicate/isHexNumberString.js'; + +describe('isHexNumberString()', () => { + it('Returns true for valid input', () => { + expect(isHexNumberString('0x0123456789abcdef')).toBeTruthy(); + expect(isHexNumberString('0X0123456789ABCDEF')).toBeTruthy(); + }); + + it('Returns false for invalid input', () => { + expect(isHexNumberString(null)).toBeFalsy(); + expect(isHexNumberString(undefined)).toBeFalsy(); + expect(isHexNumberString(Number.MAX_SAFE_INTEGER)).toBeFalsy(); + }); +}); diff --git a/test/predicate/isOctalNumberString.test.ts b/test/predicate/isOctalNumberString.test.ts new file mode 100644 index 0000000..4ca465d --- /dev/null +++ b/test/predicate/isOctalNumberString.test.ts @@ -0,0 +1,16 @@ +import { describe, it, expect } from 'vitest'; + +import { isOctalNumberString } from '../../src/predicate/isOctalNumberString.js'; + +describe('isOctalNumberString()', () => { + it('Returns true for valid input', () => { + expect(isOctalNumberString('0o01234567')).toBeTruthy(); + expect(isOctalNumberString('0O01234567')).toBeTruthy(); + }); + + it('Returns false for invalid input', () => { + expect(isOctalNumberString(null)).toBeFalsy(); + expect(isOctalNumberString(undefined)).toBeFalsy(); + expect(isOctalNumberString(Number.MAX_SAFE_INTEGER)).toBeFalsy(); + }); +}); diff --git a/test/predicate/isUnknown.test.ts b/test/predicate/isUnknown.test.ts new file mode 100644 index 0000000..4a975c4 --- /dev/null +++ b/test/predicate/isUnknown.test.ts @@ -0,0 +1,9 @@ +import { describe, it, expect } from 'vitest'; + +import { isUnknown } from '../../src/predicate/isUnknown.js'; + +describe('isUnknown()', () => { + it.each([1, 'a', true, null, {}, undefined])('Always Returns true: %j', (input) => { + expect(isUnknown(input)).toBeTruthy(); + }); +});