Compare commits
201 commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
7821031c66 | ||
|
|
a439305554 | ||
|
|
2d4956e634 | ||
|
|
48df5c429c | ||
|
|
13febb6b01 | ||
|
|
cb4ee0e92c | ||
|
|
54fc09406a | ||
|
|
63be5c0c19 | ||
|
|
ac46b5400d | ||
|
|
47415dc46a | ||
|
|
3b40955b02 | ||
|
|
faf700367e | ||
|
|
eff8e6b318 | ||
|
|
9bdcd9b57f | ||
|
|
fbcc68e139 | ||
|
|
e7c84fcb79 | ||
|
|
d22ab62991 | ||
|
|
1f2440ae0d | ||
|
|
c68ad76062 | ||
|
|
ef35cc350a | ||
|
|
882a91c54f | ||
|
|
e8e8124ba7 | ||
|
|
47a5099325 | ||
|
|
e0976457e0 | ||
|
|
5565c5e3ba | ||
|
|
ab85d9bca9 | ||
|
|
0ff273fee8 | ||
|
|
25a376875d | ||
|
|
8cbcaee674 | ||
|
|
92699e1049 | ||
|
|
47f49741ea | ||
|
|
0df21e4151 | ||
|
|
f7148e19dc | ||
|
|
a1987f8bad | ||
|
|
f2e5834421 | ||
|
|
664b9077e1 | ||
|
|
07ea404e86 | ||
|
|
e9418fe1b9 | ||
|
|
0e687a23a8 | ||
|
|
1acbd9e202 | ||
|
|
0d4cf6fcc8 | ||
|
|
f10e2caf3d | ||
|
|
e7e2213e91 | ||
|
|
9d6c91ee58 | ||
|
|
877ed1cc6a | ||
|
|
dadca59f6a | ||
|
|
68e0c95857 | ||
|
|
bcec30d735 | ||
|
|
ee79af32b6 | ||
|
|
85c89925b6 | ||
|
|
e03c249d6c | ||
|
|
5044c91273 | ||
|
|
bd5dfda993 | ||
|
|
44beb083a3 | ||
|
|
3868f47783 | ||
|
|
94dc715577 | ||
|
|
20ad8231c2 | ||
|
|
ad0c3b1429 | ||
|
|
9d26c020ee | ||
|
|
278e0e9696 | ||
|
|
1284da085f | ||
|
|
3177d11801 | ||
|
|
9265e9072d | ||
|
|
7d468191f4 | ||
|
|
d1574d358d | ||
|
|
61a437eba3 | ||
|
|
65404fbd8e | ||
|
|
888e145c5a | ||
|
|
e559b37b72 | ||
|
|
f3693674f6 | ||
|
|
911f44dc36 | ||
|
|
33cd815503 | ||
|
|
592d9093cd | ||
|
|
d3ff1fdfce | ||
|
|
45cae31f4d | ||
|
|
dc99f7cd4a | ||
|
|
e503a9ec49 | ||
|
|
06d217f70c | ||
|
|
ac7c567c2b | ||
|
|
778c5da5b3 | ||
|
|
d6fc1ce0fe | ||
|
|
c408f5a268 | ||
|
|
65ea91297e | ||
|
|
5b7ea154e6 | ||
|
|
6cbefb9af7 | ||
|
|
dc2dc9a438 | ||
|
|
23cf074a73 | ||
|
|
04ccf21dba | ||
|
|
c3d12667fd | ||
|
|
63d75d68ee | ||
|
|
73daee6648 | ||
|
|
b1efe7f5cf | ||
|
|
a5b4017d5e | ||
|
|
f5cc764e22 | ||
|
|
ad661ebcee | ||
|
|
a82aeeaa5e | ||
|
|
13b2343dfc | ||
|
|
a8de3d6f34 | ||
|
|
b007935b4b | ||
|
|
d2f98e472d | ||
|
|
6f2b24d822 | ||
|
|
238e8c80c7 | ||
|
|
b748ab72b6 | ||
|
|
5ed7e9bb40 | ||
|
|
4f8b01f2dc | ||
|
|
da6bb531af | ||
|
|
d528545e02 | ||
|
|
94749dbb2e | ||
|
|
bf6bba7af8 | ||
|
|
d8ced89efe | ||
|
|
5feadcb0b8 | ||
|
|
4c29fb35cb | ||
|
|
09d31733d3 | ||
|
|
3f93bf200d | ||
|
|
e31db97eab | ||
|
|
4b35ad5bec | ||
|
|
47fa419e4f | ||
|
|
71ca1e5573 | ||
|
|
a96abee1a3 | ||
|
|
1ffe2fb6a7 | ||
|
|
9d404cad2e | ||
|
|
fae0096eba | ||
|
|
402fbb5a7e | ||
|
|
05cdaccf92 | ||
|
|
1ff389cabb | ||
|
|
d1929ad47c | ||
|
|
f97029fd73 | ||
|
|
863e26ad6a | ||
|
|
5d0ccec21c | ||
|
|
446a7a081e | ||
|
|
d28c86ee27 | ||
|
|
11003b925e | ||
|
|
0c3f110386 | ||
|
|
c842cc260f | ||
|
|
aeb3f74d65 | ||
|
|
c25b606c3b | ||
|
|
af6b03d67f | ||
|
|
0cc82c583e | ||
|
|
8ff75abfff | ||
|
|
e7277de849 | ||
|
|
3f2caa4835 | ||
|
|
4a63743feb | ||
|
|
7c16f20d16 | ||
|
|
0f9b8479e9 | ||
|
|
0cbd9df6ce | ||
|
|
e358e44dd5 | ||
|
|
4f4820ef2f | ||
|
|
f04dffa575 | ||
|
|
ffc6ce4586 | ||
|
|
7bf407fbf1 | ||
|
|
878d111ae7 | ||
|
|
6106086a35 | ||
|
|
b064473589 | ||
|
|
04cd282265 | ||
|
|
9bc8307770 | ||
|
|
2dac8e96f4 | ||
|
|
dd2a91dce5 | ||
|
|
373605e40d | ||
|
|
120f74ab63 | ||
|
|
ea4204f0b4 | ||
|
|
28913cae88 | ||
|
|
79144f9542 | ||
|
|
c66885b781 | ||
|
|
0fff8265e6 | ||
|
|
3ec41686f7 | ||
|
|
2f5e03bed2 | ||
|
|
9683cd7fd9 | ||
|
|
3c847be5a0 | ||
|
|
2502442404 | ||
|
|
641d856b36 | ||
|
|
ab586df0f9 | ||
|
|
0e1cce5d45 | ||
|
|
4d0120adb7 | ||
|
|
6cb1d1e910 | ||
|
|
42fd8d3574 | ||
|
|
844b43c9df | ||
|
|
566f363632 | ||
|
|
2ee148f5a1 | ||
|
|
9df6f4ebe9 | ||
|
|
b8a9fb457c | ||
|
|
2232eeea79 | ||
|
|
69bbf2d4be | ||
|
|
d4869045c2 | ||
|
|
7317226c80 | ||
|
|
9ac56f1be7 | ||
|
|
c983ffa4cd | ||
|
|
45c976071a | ||
|
|
6e07df5896 | ||
|
|
65c94f1a02 | ||
|
|
442f7b709f | ||
|
|
ad6f372c47 | ||
|
|
36b46f1889 | ||
|
|
b2bb3e7d37 | ||
|
|
3bdaf21475 | ||
|
|
c8736c2972 | ||
|
|
c84c2cbeca | ||
|
|
d11b7eaaa1 | ||
|
|
ff268de3d0 | ||
|
|
fe754b5528 | ||
|
|
0988832ef7 | ||
|
|
db25d554dc |
19 changed files with 6329 additions and 1353 deletions
3
.gitattributes
vendored
3
.gitattributes
vendored
|
|
@ -1,2 +1 @@
|
|||
* text=auto
|
||||
*.js text eol=lf
|
||||
* text=auto eol=lf
|
||||
|
|
|
|||
3
.github/security.md
vendored
Normal file
3
.github/security.md
vendored
Normal file
|
|
@ -0,0 +1,3 @@
|
|||
# Security Policy
|
||||
|
||||
To report a security vulnerability, please use the [Tidelift security contact](https://tidelift.com/security). Tidelift will coordinate the fix and disclosure.
|
||||
21
.github/workflows/main.yml
vendored
Normal file
21
.github/workflows/main.yml
vendored
Normal file
|
|
@ -0,0 +1,21 @@
|
|||
name: CI
|
||||
on:
|
||||
- push
|
||||
- pull_request
|
||||
jobs:
|
||||
test:
|
||||
name: Node.js ${{ matrix.node-version }}
|
||||
runs-on: ubuntu-latest
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
node-version:
|
||||
- 24
|
||||
- 22
|
||||
steps:
|
||||
- uses: actions/checkout@v6
|
||||
- uses: actions/setup-node@v6
|
||||
with:
|
||||
node-version: ${{ matrix.node-version }}
|
||||
- run: npm install
|
||||
- run: npm test
|
||||
3
.gitignore
vendored
3
.gitignore
vendored
|
|
@ -1,3 +1,4 @@
|
|||
node_modules
|
||||
yarn.lock
|
||||
dist
|
||||
/distribution
|
||||
.tsimp
|
||||
|
|
|
|||
|
|
@ -1,4 +0,0 @@
|
|||
language: node_js
|
||||
node_js:
|
||||
- '8'
|
||||
- '6'
|
||||
9
AGENTS.md
Normal file
9
AGENTS.md
Normal file
|
|
@ -0,0 +1,9 @@
|
|||
# Notes
|
||||
|
||||
## Branded types for type guards
|
||||
|
||||
TypeScript type guards narrow in both branches. If `is.integer(n)` returns `value is number` and the input is `number`, the false branch computes `Exclude<number, number>` = `never`. This makes common patterns like `if (!is.integer(n)) throw; use(n)` fail because `n` becomes `never` after the guard.
|
||||
|
||||
To avoid this, type guard predicates use branded types (e.g., `number & {readonly __brand: 'Integer'}`, `string & {readonly __brand: 'UrlString'}`). A branded subtype ensures the false branch stays the original type (e.g., `Exclude<number, Integer>` = `number`).
|
||||
|
||||
Assert functions (`asserts value is T`) don't need branded types since they throw on failure and have no false branch. They use plain types like `asserts value is number`.
|
||||
1
CLAUDE.md
Symbolic link
1
CLAUDE.md
Symbolic link
|
|
@ -0,0 +1 @@
|
|||
AGENTS.md
|
||||
2
license
2
license
|
|
@ -1,6 +1,6 @@
|
|||
MIT License
|
||||
|
||||
Copyright (c) Sindre Sorhus <sindresorhus@gmail.com> (sindresorhus.com)
|
||||
Copyright (c) Sindre Sorhus <sindresorhus@gmail.com> (https://sindresorhus.com)
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
|
||||
|
||||
|
|
|
|||
67
package.json
67
package.json
|
|
@ -1,26 +1,31 @@
|
|||
{
|
||||
"name": "@sindresorhus/is",
|
||||
"version": "0.9.0",
|
||||
"description": "Type check values: `is.string('🦄') //=> true`",
|
||||
"version": "8.1.0",
|
||||
"description": "Type check values",
|
||||
"license": "MIT",
|
||||
"repository": "sindresorhus/is",
|
||||
"funding": "https://github.com/sindresorhus/is?sponsor=1",
|
||||
"author": {
|
||||
"name": "Sindre Sorhus",
|
||||
"email": "sindresorhus@gmail.com",
|
||||
"url": "sindresorhus.com"
|
||||
"url": "https://sindresorhus.com"
|
||||
},
|
||||
"main": "dist/index.js",
|
||||
"type": "module",
|
||||
"exports": {
|
||||
"types": "./distribution/index.d.ts",
|
||||
"default": "./distribution/index.js"
|
||||
},
|
||||
"sideEffects": false,
|
||||
"engines": {
|
||||
"node": ">=6"
|
||||
"node": ">=22"
|
||||
},
|
||||
"scripts": {
|
||||
"lint": "tslint --format stylish --project .",
|
||||
"build": "tsc",
|
||||
"test": "npm run lint && npm run build && ava dist/tests",
|
||||
"prepublish": "npm run build && del dist/tests"
|
||||
"build": "del distribution && tsc",
|
||||
"test": "tsc --noEmit && tsc --project test/tsconfig.json --noEmit && xo && node --experimental-transform-types --test test/test.ts",
|
||||
"prepare": "npm run build"
|
||||
},
|
||||
"files": [
|
||||
"dist"
|
||||
"distribution"
|
||||
],
|
||||
"keywords": [
|
||||
"type",
|
||||
|
|
@ -41,25 +46,31 @@
|
|||
"kind",
|
||||
"primitive",
|
||||
"verify",
|
||||
"compare"
|
||||
"compare",
|
||||
"typescript",
|
||||
"typeguards",
|
||||
"types"
|
||||
],
|
||||
"dependencies": {
|
||||
"symbol-observable": "^1.2.0"
|
||||
"xo": {
|
||||
"rules": {
|
||||
"@typescript-eslint/no-unsafe-enum-comparison": "off",
|
||||
"@typescript-eslint/no-confusing-void-expression": "off",
|
||||
"@typescript-eslint/no-unsafe-type-assertion": "off",
|
||||
"@stylistic/operator-linebreak": "off"
|
||||
}
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/jsdom": "^11.0.4",
|
||||
"@types/node": "^9.6.0",
|
||||
"@types/tempy": "^0.1.0",
|
||||
"@types/zen-observable": "^0.5.3",
|
||||
"ava": "*",
|
||||
"del-cli": "^1.1.0",
|
||||
"jsdom": "^11.6.2",
|
||||
"rxjs": "^6.0.0",
|
||||
"tempy": "^0.2.1",
|
||||
"tslint": "^5.9.1",
|
||||
"tslint-xo": "^0.7.0",
|
||||
"typescript": "^2.8.1",
|
||||
"zen-observable": "^0.8.8"
|
||||
},
|
||||
"types": "dist/index.d.ts"
|
||||
"@sindresorhus/tsconfig": "^8.1.0",
|
||||
"@types/jsdom": "^28.0.1",
|
||||
"@types/node": "^25.5.2",
|
||||
"@types/zen-observable": "^0.8.7",
|
||||
"del-cli": "^7.0.0",
|
||||
"expect-type": "^1.3.0",
|
||||
"jsdom": "^29.0.2",
|
||||
"rxjs": "^7.8.2",
|
||||
"tempy": "^3.2.0",
|
||||
"typescript": "6.0.2",
|
||||
"xo": "^2.0.2",
|
||||
"zen-observable": "^0.10.0"
|
||||
}
|
||||
}
|
||||
|
|
|
|||
2263
source/index.ts
2263
source/index.ts
File diff suppressed because it is too large
Load diff
|
|
@ -1,781 +0,0 @@
|
|||
import * as fs from 'fs';
|
||||
import * as net from 'net';
|
||||
import * as Stream from 'stream';
|
||||
import * as util from 'util';
|
||||
import * as tempy from 'tempy';
|
||||
import test, {TestContext, Context} from 'ava';
|
||||
import {JSDOM} from 'jsdom';
|
||||
import {Subject, Observable} from 'rxjs';
|
||||
import ZenObservable from 'zen-observable';
|
||||
import m from '..';
|
||||
|
||||
const isNode8orHigher = Number(process.versions.node.split('.')[0]) >= 8;
|
||||
|
||||
class PromiseSubclassFixture<T> extends Promise<T> {}
|
||||
class ErrorSubclassFixture extends Error {}
|
||||
|
||||
const {window} = new JSDOM();
|
||||
const {document} = window;
|
||||
const createDomElement = (el: string) => document.createElement(el);
|
||||
|
||||
interface Test {
|
||||
is(value: any): boolean;
|
||||
fixtures: any[];
|
||||
}
|
||||
|
||||
const types = new Map<string, Test>([
|
||||
['undefined', {
|
||||
is: m.undefined,
|
||||
fixtures: [
|
||||
undefined
|
||||
]
|
||||
}],
|
||||
['null', {
|
||||
is: m.null_,
|
||||
fixtures: [
|
||||
null
|
||||
]
|
||||
}],
|
||||
['string', {
|
||||
is: m.string,
|
||||
fixtures: [
|
||||
'🦄',
|
||||
'hello world',
|
||||
''
|
||||
]
|
||||
}],
|
||||
['number', {
|
||||
is: m.number,
|
||||
fixtures: [
|
||||
6,
|
||||
1.4,
|
||||
0,
|
||||
-0,
|
||||
Infinity,
|
||||
-Infinity
|
||||
]
|
||||
}],
|
||||
['boolean', {
|
||||
is: m.boolean,
|
||||
fixtures: [
|
||||
true, false
|
||||
]
|
||||
}],
|
||||
['symbol', {
|
||||
is: m.symbol,
|
||||
fixtures: [
|
||||
Symbol('🦄')
|
||||
]
|
||||
}],
|
||||
['array', {
|
||||
is: m.array,
|
||||
fixtures: [
|
||||
[1, 2],
|
||||
new Array(2) // tslint:disable-line:prefer-array-literal
|
||||
]
|
||||
}],
|
||||
['function', {
|
||||
is: m.function_,
|
||||
fixtures: [
|
||||
// tslint:disable:no-unused no-empty no-unused-variable only-arrow-functions no-function-expression
|
||||
function foo() {},
|
||||
function () {},
|
||||
() => {},
|
||||
async function () {},
|
||||
function * (): any {}
|
||||
// tslint:enable:no-unused no-empty no-unused-variable only-arrow-functions no-function-expression
|
||||
]
|
||||
}],
|
||||
['buffer', {
|
||||
is: m.buffer,
|
||||
fixtures: [
|
||||
Buffer.from('🦄')
|
||||
]
|
||||
}],
|
||||
['object', {
|
||||
is: m.object,
|
||||
fixtures: [
|
||||
{x: 1},
|
||||
Object.create({x: 1})
|
||||
]
|
||||
}],
|
||||
['regExp', {
|
||||
is: m.regExp,
|
||||
fixtures: [
|
||||
/\w/,
|
||||
new RegExp('\\w')
|
||||
]
|
||||
}],
|
||||
['date', {
|
||||
is: m.date,
|
||||
fixtures: [
|
||||
new Date()
|
||||
]
|
||||
}],
|
||||
['error', {
|
||||
is: m.error,
|
||||
fixtures: [
|
||||
new Error('🦄'),
|
||||
new ErrorSubclassFixture()
|
||||
]
|
||||
}],
|
||||
['nativePromise', {
|
||||
is: m.nativePromise,
|
||||
fixtures: [
|
||||
Promise.resolve(),
|
||||
PromiseSubclassFixture.resolve()
|
||||
]
|
||||
}],
|
||||
['promise', {
|
||||
is: m.promise,
|
||||
fixtures: [
|
||||
{then() {}, catch() {}} // tslint:disable-line:no-empty
|
||||
]
|
||||
}],
|
||||
['generator', {
|
||||
is: m.generator,
|
||||
fixtures: [
|
||||
(function * () {
|
||||
yield 4;
|
||||
})()
|
||||
]
|
||||
}],
|
||||
['generatorFunction', {
|
||||
is: m.generatorFunction,
|
||||
fixtures: [
|
||||
function * () {
|
||||
yield 4;
|
||||
}
|
||||
]
|
||||
}],
|
||||
['asyncFunction', {
|
||||
is: m.asyncFunction,
|
||||
fixtures: [
|
||||
async function () {}, // tslint:disable-line:no-empty only-arrow-functions no-function-expression
|
||||
async () => {} // tslint:disable-line:no-empty
|
||||
]
|
||||
}],
|
||||
['boundFunction', {
|
||||
is: m.boundFunction,
|
||||
fixtures: [
|
||||
() => {}, // tslint:disable-line:no-empty
|
||||
function () {}.bind(null), // tslint:disable-line:no-empty only-arrow-functions
|
||||
]
|
||||
}],
|
||||
['map', {
|
||||
is: m.map,
|
||||
fixtures: [
|
||||
new Map()
|
||||
]
|
||||
}],
|
||||
['set', {
|
||||
is: m.set,
|
||||
fixtures: [
|
||||
new Set()
|
||||
]
|
||||
}],
|
||||
['weakSet', {
|
||||
is: m.weakSet,
|
||||
fixtures: [
|
||||
new WeakSet()
|
||||
]
|
||||
}],
|
||||
['weakMap', {
|
||||
is: m.weakMap,
|
||||
fixtures: [
|
||||
new WeakMap()
|
||||
]
|
||||
}],
|
||||
['int8Array', {
|
||||
is: m.int8Array,
|
||||
fixtures: [
|
||||
new Int8Array(0)
|
||||
]
|
||||
}],
|
||||
['uint8Array', {
|
||||
is: m.uint8Array,
|
||||
fixtures: [
|
||||
new Uint8Array(0)
|
||||
]
|
||||
}],
|
||||
['uint8ClampedArray', {
|
||||
is: m.uint8ClampedArray,
|
||||
fixtures: [
|
||||
new Uint8ClampedArray(0)
|
||||
]
|
||||
}],
|
||||
['int16Array', {
|
||||
is: m.int16Array,
|
||||
fixtures: [
|
||||
new Int16Array(0)
|
||||
]
|
||||
}],
|
||||
['uint16Array', {
|
||||
is: m.uint16Array,
|
||||
fixtures: [
|
||||
new Uint16Array(0)
|
||||
]
|
||||
}],
|
||||
['int32Array', {
|
||||
is: m.int32Array,
|
||||
fixtures: [
|
||||
new Int32Array(0)
|
||||
]
|
||||
}],
|
||||
['uint32Array', {
|
||||
is: m.uint32Array,
|
||||
fixtures: [
|
||||
new Uint32Array(0)
|
||||
]
|
||||
}],
|
||||
['float32Array', {
|
||||
is: m.float32Array,
|
||||
fixtures: [
|
||||
new Float32Array(0)
|
||||
]
|
||||
}],
|
||||
['float64Array', {
|
||||
is: m.float64Array,
|
||||
fixtures: [
|
||||
new Float64Array(0)
|
||||
]
|
||||
}],
|
||||
['arrayBuffer', {
|
||||
is: m.arrayBuffer,
|
||||
fixtures: [
|
||||
new ArrayBuffer(10)
|
||||
]
|
||||
}],
|
||||
['dataView', {
|
||||
is: m.dataView,
|
||||
fixtures: [
|
||||
new DataView(new ArrayBuffer(10))
|
||||
]
|
||||
}],
|
||||
['nan', {
|
||||
is: m.nan,
|
||||
fixtures: [
|
||||
NaN,
|
||||
Number.NaN
|
||||
]
|
||||
}],
|
||||
['nullOrUndefined', {
|
||||
is: m.nullOrUndefined,
|
||||
fixtures: [
|
||||
null,
|
||||
undefined
|
||||
]
|
||||
}],
|
||||
['plainObject', {
|
||||
is: m.plainObject,
|
||||
fixtures: [
|
||||
{x: 1},
|
||||
Object.create(null),
|
||||
new Object()
|
||||
]
|
||||
}],
|
||||
['integer', {
|
||||
is: m.integer,
|
||||
fixtures: [
|
||||
6
|
||||
]
|
||||
}],
|
||||
['safeInteger', {
|
||||
is: m.safeInteger,
|
||||
fixtures: [
|
||||
Math.pow(2, 53) - 1,
|
||||
-Math.pow(2, 53) + 1
|
||||
]
|
||||
}],
|
||||
['domElement', {
|
||||
is: m.domElement,
|
||||
fixtures: [
|
||||
'div',
|
||||
'input',
|
||||
'span',
|
||||
'img',
|
||||
'canvas',
|
||||
'script'
|
||||
].map(createDomElement) }
|
||||
],
|
||||
['non-domElements', {
|
||||
is: value => !m.domElement(value),
|
||||
fixtures: [
|
||||
document.createTextNode('data'),
|
||||
document.createProcessingInstruction('xml-stylesheet', 'href="mycss.css" type="text/css"'),
|
||||
document.createComment('This is a comment'),
|
||||
document,
|
||||
document.implementation.createDocumentType('svg:svg', '-//W3C//DTD SVG 1.1//EN', 'https://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd'),
|
||||
document.createDocumentFragment()
|
||||
]
|
||||
}],
|
||||
['nodeStream', {
|
||||
is: m.nodeStream,
|
||||
fixtures: [
|
||||
fs.createReadStream('readme.md'),
|
||||
fs.createWriteStream(tempy.file()),
|
||||
new net.Socket(),
|
||||
new Stream.Duplex(),
|
||||
new Stream.PassThrough(),
|
||||
new Stream.Readable(),
|
||||
new Stream.Transform(),
|
||||
new Stream.Stream(),
|
||||
new Stream.Writable()
|
||||
]
|
||||
}],
|
||||
['observable', {
|
||||
is: m.observable,
|
||||
fixtures: [
|
||||
new Observable(),
|
||||
new Subject(),
|
||||
new ZenObservable(() => {}) // tslint:disable-line:no-empty
|
||||
]
|
||||
}],
|
||||
['infinite', {
|
||||
is: m.infinite,
|
||||
fixtures: [
|
||||
Infinity,
|
||||
-Infinity
|
||||
]
|
||||
}]
|
||||
]);
|
||||
|
||||
// This ensures a certain method matches only the types it's supposed to and none of the other methods' types
|
||||
const testType = (t: TestContext & Context<any>, type: string, exclude?: string[]) => {
|
||||
const testData = types.get(type);
|
||||
|
||||
if (testData === undefined) {
|
||||
t.fail(`is.${type} not defined`);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
const {is} = testData;
|
||||
|
||||
for (const [key, {fixtures}] of types) {
|
||||
// TODO: Automatically exclude value types in other tests that we have in the current one.
|
||||
// Could reduce the use of `exclude`.
|
||||
if (exclude && exclude.indexOf(key) !== -1) {
|
||||
continue;
|
||||
}
|
||||
|
||||
const assert = key === type ? t.true.bind(t) : t.false.bind(t);
|
||||
|
||||
for (const fixture of fixtures) {
|
||||
assert(is(fixture), `Value: ${util.inspect(fixture)}`);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
test('is', t => {
|
||||
t.is(m(null), 'null');
|
||||
t.is(m(undefined), 'undefined');
|
||||
|
||||
// TODO: Expand this to all the supported types. Maybe reuse `testType()` somehow.
|
||||
});
|
||||
|
||||
test('is.undefined', t => {
|
||||
testType(t, 'undefined', ['nullOrUndefined']);
|
||||
});
|
||||
|
||||
test('is.null', t => {
|
||||
testType(t, 'null', ['nullOrUndefined']);
|
||||
});
|
||||
|
||||
test('is.string', t => {
|
||||
testType(t, 'string');
|
||||
});
|
||||
|
||||
test('is.number', t => {
|
||||
testType(t, 'number', ['nan', 'integer', 'safeInteger', 'infinite']);
|
||||
});
|
||||
|
||||
test('is.boolean', t => {
|
||||
testType(t, 'boolean');
|
||||
});
|
||||
|
||||
test('is.symbol', t => {
|
||||
testType(t, 'symbol');
|
||||
});
|
||||
|
||||
test('is.array', t => {
|
||||
testType(t, 'array');
|
||||
});
|
||||
|
||||
test('is.function', t => {
|
||||
testType(t, 'function', ['generatorFunction', 'asyncFunction', 'boundFunction']);
|
||||
});
|
||||
|
||||
test('is.boundFunction', t => {
|
||||
t.false(m.boundFunction(function () {})); // tslint:disable-line:no-empty only-arrow-functions
|
||||
});
|
||||
|
||||
test('is.buffer', t => {
|
||||
testType(t, 'buffer');
|
||||
});
|
||||
|
||||
test('is.object', t => {
|
||||
const testData = types.get('object');
|
||||
|
||||
if (testData === undefined) {
|
||||
t.fail('is.object not defined');
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
for (const el of testData.fixtures) {
|
||||
t.true(m.object(el));
|
||||
}
|
||||
});
|
||||
|
||||
test('is.regExp', t => {
|
||||
testType(t, 'regExp');
|
||||
});
|
||||
|
||||
test('is.date', t => {
|
||||
testType(t, 'date');
|
||||
});
|
||||
|
||||
test('is.error', t => {
|
||||
testType(t, 'error');
|
||||
});
|
||||
|
||||
if (isNode8orHigher) {
|
||||
test('is.nativePromise', t => {
|
||||
testType(t, 'nativePromise');
|
||||
});
|
||||
|
||||
test('is.promise', t => {
|
||||
testType(t, 'promise', ['nativePromise']);
|
||||
});
|
||||
|
||||
/*test('is.asyncFunction', t => {
|
||||
testType(t, 'asyncFunction', ['function']);
|
||||
});*/
|
||||
}
|
||||
|
||||
test('is.generator', t => {
|
||||
testType(t, 'generator');
|
||||
});
|
||||
|
||||
test('is.generatorFunction', t => {
|
||||
testType(t, 'generatorFunction', ['function']);
|
||||
});
|
||||
|
||||
test('is.map', t => {
|
||||
testType(t, 'map');
|
||||
});
|
||||
|
||||
test('is.set', t => {
|
||||
testType(t, 'set');
|
||||
});
|
||||
|
||||
test('is.weakMap', t => {
|
||||
testType(t, 'weakMap');
|
||||
});
|
||||
|
||||
test('is.weakSet', t => {
|
||||
testType(t, 'weakSet');
|
||||
});
|
||||
|
||||
test('is.int8Array', t => {
|
||||
testType(t, 'int8Array');
|
||||
});
|
||||
|
||||
test('is.uint8Array', t => {
|
||||
testType(t, 'uint8Array', ['buffer']);
|
||||
});
|
||||
|
||||
test('is.uint8ClampedArray', t => {
|
||||
testType(t, 'uint8ClampedArray');
|
||||
});
|
||||
|
||||
test('is.int16Array', t => {
|
||||
testType(t, 'int16Array');
|
||||
});
|
||||
|
||||
test('is.uint16Array', t => {
|
||||
testType(t, 'uint16Array');
|
||||
});
|
||||
|
||||
test('is.int32Array', t => {
|
||||
testType(t, 'int32Array');
|
||||
});
|
||||
|
||||
test('is.uint32Array', t => {
|
||||
testType(t, 'uint32Array');
|
||||
});
|
||||
|
||||
test('is.float32Array', t => {
|
||||
testType(t, 'float32Array');
|
||||
});
|
||||
|
||||
test('is.float64Array', t => {
|
||||
testType(t, 'float64Array');
|
||||
});
|
||||
|
||||
test('is.arrayBuffer', t => {
|
||||
testType(t, 'arrayBuffer');
|
||||
});
|
||||
|
||||
test('is.dataView', t => {
|
||||
testType(t, 'dataView');
|
||||
});
|
||||
|
||||
test('is.directInstanceOf', t => {
|
||||
const error = new Error();
|
||||
const errorSubclass = new ErrorSubclassFixture();
|
||||
|
||||
t.true(m.directInstanceOf(error, Error));
|
||||
t.true(m.directInstanceOf(errorSubclass, ErrorSubclassFixture));
|
||||
|
||||
t.false(m.directInstanceOf(error, ErrorSubclassFixture));
|
||||
t.false(m.directInstanceOf(errorSubclass, Error));
|
||||
});
|
||||
|
||||
test('is.truthy', t => {
|
||||
t.true(m.truthy('unicorn'));
|
||||
t.true(m.truthy('🦄'));
|
||||
t.true(m.truthy(new Set()));
|
||||
t.true(m.truthy(Symbol('🦄')));
|
||||
t.true(m.truthy(true));
|
||||
});
|
||||
|
||||
test('is.falsy', t => {
|
||||
t.true(m.falsy(false));
|
||||
t.true(m.falsy(0));
|
||||
t.true(m.falsy(''));
|
||||
t.true(m.falsy(null));
|
||||
t.true(m.falsy(undefined));
|
||||
t.true(m.falsy(NaN));
|
||||
});
|
||||
|
||||
test('is.nan', t => {
|
||||
testType(t, 'nan');
|
||||
});
|
||||
|
||||
test('is.nullOrUndefined', t => {
|
||||
testType(t, 'nullOrUndefined', ['undefined', 'null']);
|
||||
});
|
||||
|
||||
test('is.primitive', t => {
|
||||
const primitives = [
|
||||
undefined,
|
||||
null,
|
||||
'🦄',
|
||||
6,
|
||||
Infinity,
|
||||
-Infinity,
|
||||
true,
|
||||
false,
|
||||
Symbol('🦄')
|
||||
];
|
||||
|
||||
for (const el of primitives) {
|
||||
t.true(m.primitive(el));
|
||||
}
|
||||
});
|
||||
|
||||
test('is.integer', t => {
|
||||
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']);
|
||||
});
|
||||
|
||||
test('is.iterable', t => {
|
||||
t.true(m.iterable(''));
|
||||
t.true(m.iterable([]));
|
||||
t.true(m.iterable(new Map()));
|
||||
t.false(m.iterable(null));
|
||||
t.false(m.iterable(undefined));
|
||||
t.false(m.iterable(0));
|
||||
t.false(m.iterable(NaN));
|
||||
t.false(m.iterable(Infinity));
|
||||
t.false(m.iterable({}));
|
||||
});
|
||||
|
||||
test('is.class', t => {
|
||||
class Foo {} // tslint:disable-line
|
||||
const classDeclarations = [
|
||||
Foo,
|
||||
class Bar extends Foo {} // tslint:disable-line
|
||||
];
|
||||
|
||||
for (const x of classDeclarations) {
|
||||
t.true(m.class_(x));
|
||||
}
|
||||
});
|
||||
|
||||
test('is.typedArray', t => {
|
||||
// Typescript currently does not support empty constructors for these
|
||||
// See https://github.com/Microsoft/TypeScript/issues/19680
|
||||
const typedArrays = [
|
||||
new Int8Array(0),
|
||||
new Uint8Array(0),
|
||||
new Uint8ClampedArray(0),
|
||||
new Uint16Array(0),
|
||||
new Int32Array(0),
|
||||
new Uint32Array(0),
|
||||
new Float32Array(0),
|
||||
new Float64Array(0)
|
||||
];
|
||||
|
||||
for (const el of typedArrays) {
|
||||
t.true(m.typedArray(el));
|
||||
}
|
||||
|
||||
t.false(m.typedArray(new ArrayBuffer(1)));
|
||||
t.false(m.typedArray([]));
|
||||
t.false(m.typedArray({}));
|
||||
});
|
||||
|
||||
test('is.arrayLike', t => {
|
||||
(function () { // tslint:disable-line:only-arrow-functions
|
||||
t.true(m.arrayLike(arguments));
|
||||
})();
|
||||
t.true(m.arrayLike([]));
|
||||
t.true(m.arrayLike('unicorn'));
|
||||
|
||||
t.false(m.arrayLike({}));
|
||||
t.false(m.arrayLike(() => {})); // tslint:disable-line:no-empty
|
||||
t.false(m.arrayLike(new Map()));
|
||||
});
|
||||
|
||||
test('is.inRange', t => {
|
||||
const x = 3;
|
||||
|
||||
t.true(m.inRange(x, [0, 5]));
|
||||
t.true(m.inRange(x, [5, 0]));
|
||||
t.true(m.inRange(x, [-5, 5]));
|
||||
t.true(m.inRange(x, [5, -5]));
|
||||
t.false(m.inRange(x, [4, 8]));
|
||||
t.true(m.inRange(-7, [-5, -10]));
|
||||
t.true(m.inRange(-5, [-5, -10]));
|
||||
t.true(m.inRange(-10, [-5, -10]));
|
||||
|
||||
t.true(m.inRange(x, 10));
|
||||
t.true(m.inRange(0, 0));
|
||||
t.true(m.inRange(-2, -3));
|
||||
t.false(m.inRange(x, 2));
|
||||
t.false(m.inRange(-3, -2));
|
||||
|
||||
t.throws(() => {
|
||||
m.inRange(0, []);
|
||||
});
|
||||
|
||||
t.throws(() => {
|
||||
m.inRange(0, [5]);
|
||||
});
|
||||
|
||||
t.throws(() => {
|
||||
m.inRange(0, [1, 2, 3]);
|
||||
});
|
||||
});
|
||||
|
||||
test('is.domElement', t => {
|
||||
testType(t, 'domElement');
|
||||
t.false(m.domElement({nodeType: 1, nodeName: 'div'}));
|
||||
});
|
||||
|
||||
test('is.nodeStream', t => {
|
||||
testType(t, 'nodeStream');
|
||||
});
|
||||
|
||||
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));
|
||||
|
||||
t.true(m.empty(false));
|
||||
t.false(m.empty(true));
|
||||
|
||||
t.true(m.empty(''));
|
||||
t.false(m.empty('🦄'));
|
||||
|
||||
t.true(m.empty([]));
|
||||
t.false(m.empty(['🦄']));
|
||||
|
||||
t.true(m.empty({}));
|
||||
t.false(m.empty({unicorn: '🦄'}));
|
||||
|
||||
const tempMap = new Map();
|
||||
t.true(m.empty(tempMap));
|
||||
|
||||
tempMap.set('unicorn', '🦄');
|
||||
t.false(m.empty(tempMap));
|
||||
|
||||
const tempSet = new Set();
|
||||
t.true(m.empty(tempSet));
|
||||
|
||||
tempSet.add(1);
|
||||
t.false(m.empty(tempSet));
|
||||
});
|
||||
|
||||
test('is.emptyOrWhitespace', t => {
|
||||
t.true(m.emptyOrWhitespace(''));
|
||||
t.true(m.emptyOrWhitespace(' '));
|
||||
t.false(m.emptyOrWhitespace('🦄'));
|
||||
t.false(m.emptyOrWhitespace('unicorn'));
|
||||
});
|
||||
|
||||
test('is.any', t => {
|
||||
t.true(m.any(m.string, {}, true, '🦄'));
|
||||
t.true(m.any(m.object, false, {}, 'unicorns'));
|
||||
t.false(m.any(m.boolean, '🦄', [], 3));
|
||||
t.false(m.any(m.integer, true, 'lol', {}));
|
||||
|
||||
t.throws(() => {
|
||||
m.any(null, true);
|
||||
});
|
||||
|
||||
t.throws(() => {
|
||||
m.any(m.string);
|
||||
});
|
||||
});
|
||||
|
||||
test('is.all', t => {
|
||||
t.true(m.all(m.object, {}, new Set(), new Map()));
|
||||
t.true(m.all(m.boolean, true, false));
|
||||
t.false(m.all(m.string, '🦄', []));
|
||||
t.false(m.all(m.set, new Map(), {}));
|
||||
|
||||
t.throws(() => {
|
||||
m.all(null, true);
|
||||
});
|
||||
|
||||
t.throws(() => {
|
||||
m.all(m.string);
|
||||
});
|
||||
});
|
||||
199
source/types.ts
Normal file
199
source/types.ts
Normal file
|
|
@ -0,0 +1,199 @@
|
|||
// Extracted from https://github.com/sindresorhus/type-fest/blob/78019f42ea888b0cdceb41a4a78163868de57555/index.d.ts
|
||||
|
||||
/**
|
||||
Matches any [primitive value](https://developer.mozilla.org/en-US/docs/Glossary/Primitive).
|
||||
*/
|
||||
export type Primitive =
|
||||
// eslint-disable-next-line @typescript-eslint/no-restricted-types
|
||||
| null
|
||||
| undefined
|
||||
| string
|
||||
| number
|
||||
| boolean
|
||||
| symbol
|
||||
| bigint;
|
||||
|
||||
/**
|
||||
Matches a [`class` constructor](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Classes).
|
||||
*/
|
||||
type Constructor<T, Arguments extends unknown[] = any[]> = new(...arguments_: Arguments) => T;
|
||||
|
||||
/**
|
||||
Matches a [`class`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Classes).
|
||||
*/
|
||||
export type Class<T, Arguments extends unknown[] = any[]> = Constructor<T, Arguments> & {prototype: T};
|
||||
|
||||
/**
|
||||
Matches any [typed array](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/TypedArray), like `Uint8Array` or `Float64Array`.
|
||||
*/
|
||||
export type TypedArray =
|
||||
| Int8Array
|
||||
| Uint8Array
|
||||
| Uint8ClampedArray
|
||||
| Int16Array
|
||||
| Uint16Array
|
||||
| Int32Array
|
||||
| Uint32Array
|
||||
| Float32Array
|
||||
| Float64Array
|
||||
| BigInt64Array
|
||||
| BigUint64Array;
|
||||
|
||||
declare global {
|
||||
// eslint-disable-next-line @typescript-eslint/consistent-type-definitions -- This must be an `interface` so it can be merged.
|
||||
interface SymbolConstructor {
|
||||
readonly observable: symbol;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
Matches a value that is like an [Observable](https://github.com/tc39/proposal-observable).
|
||||
*/
|
||||
export type ObservableLike = {
|
||||
subscribe(observer: (value: unknown) => void): void;
|
||||
[Symbol.observable](): ObservableLike;
|
||||
};
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/no-restricted-types
|
||||
export type Falsy = false | 0 | 0n | '' | null | undefined;
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/no-restricted-types
|
||||
export type WeakRef<T extends object> = {
|
||||
readonly [Symbol.toStringTag]: 'WeakRef';
|
||||
deref(): T | undefined;
|
||||
};
|
||||
|
||||
export type ArrayLike<T> = {
|
||||
readonly [index: number]: T;
|
||||
readonly length: number;
|
||||
};
|
||||
|
||||
export type NodeStream = {
|
||||
pipe<T extends NodeJS.WritableStream>(destination: T, options?: {end?: boolean}): T;
|
||||
} & NodeJS.EventEmitter;
|
||||
|
||||
export type Predicate = (value: unknown) => boolean;
|
||||
|
||||
export type NonEmptyString = string & {0: string};
|
||||
|
||||
export type Whitespace = ' ';
|
||||
|
||||
type Brand<Key extends string> = Readonly<Record<Key, true>>;
|
||||
|
||||
/**
|
||||
A string that represents a valid URL.
|
||||
|
||||
This is a branded type to prevent incorrect TypeScript type narrowing.
|
||||
*/
|
||||
export type UrlString = string & {readonly __brand: 'UrlString'};
|
||||
|
||||
// Keep numeric guards branded and simple. This intentionally favors correct false-branch narrowing for `number` inputs over perfect success-branch narrowing for numeric literal unions.
|
||||
|
||||
/**
|
||||
The IEEE 754 "Not-a-Number" value, typed as a subtype of `number`.
|
||||
|
||||
Branded to prevent false-branch narrowing to `never` when the input is `number`.
|
||||
*/
|
||||
export type NaN = number & Brand<'__nanBrand'>;
|
||||
|
||||
/**
|
||||
A finite number (excludes `NaN`, `Infinity`, and `-Infinity`).
|
||||
|
||||
Branded to prevent false-branch narrowing to `never` when the input is `number`.
|
||||
*/
|
||||
export type FiniteNumber = number & Brand<'__finiteNumberBrand'>;
|
||||
|
||||
/**
|
||||
A number greater than or equal to zero.
|
||||
|
||||
Branded to prevent false-branch narrowing to `never` when the input is `number`.
|
||||
*/
|
||||
export type NonNegativeNumber = number & Brand<'__nonNegativeNumberBrand'>;
|
||||
|
||||
/**
|
||||
An integer value (no fractional part).
|
||||
|
||||
Branded to prevent false-branch narrowing to `never` when the input is `number`.
|
||||
*/
|
||||
export type Integer = FiniteNumber & Brand<'__integerBrand'>;
|
||||
|
||||
/**
|
||||
A number greater than zero.
|
||||
|
||||
Branded to prevent false-branch narrowing to `never` when the input is `number`.
|
||||
*/
|
||||
export type PositiveNumber = NonNegativeNumber & Brand<'__positiveNumberBrand'>;
|
||||
|
||||
/**
|
||||
A number less than zero.
|
||||
|
||||
Branded to prevent false-branch narrowing to `never` when the input is `number`.
|
||||
*/
|
||||
export type NegativeNumber = number & Brand<'__negativeNumberBrand'>;
|
||||
|
||||
/**
|
||||
An integer less than zero.
|
||||
|
||||
Branded to prevent false-branch narrowing to `never` when the input is `number`.
|
||||
*/
|
||||
export type NegativeInteger = Integer & NegativeNumber & Brand<'__negativeIntegerBrand'>;
|
||||
|
||||
/**
|
||||
An integer greater than or equal to zero.
|
||||
|
||||
Branded to prevent false-branch narrowing to `never` when the input is `number`.
|
||||
*/
|
||||
export type NonNegativeInteger = Integer & NonNegativeNumber & Brand<'__nonNegativeIntegerBrand'>;
|
||||
|
||||
/**
|
||||
An integer greater than zero.
|
||||
|
||||
Branded to prevent false-branch narrowing to `never` when the input is `number`.
|
||||
*/
|
||||
export type PositiveInteger = NonNegativeInteger & PositiveNumber & Brand<'__positiveIntegerBrand'>;
|
||||
|
||||
// Note: type-fest uses the `1e999` overflow trick to represent these types (since TypeScript has
|
||||
// no built-in Infinity type), but we use branded types here for consistency and to avoid
|
||||
// relying on numeric overflow behavior.
|
||||
|
||||
/**
|
||||
A positive infinite number (`Infinity`).
|
||||
|
||||
Branded to prevent false-branch narrowing to `never` when the input is `number`.
|
||||
*/
|
||||
export type PositiveInfinity = PositiveNumber & Brand<'__positiveInfinityBrand'>;
|
||||
|
||||
/**
|
||||
A negative infinite number (`-Infinity`).
|
||||
|
||||
Branded to prevent false-branch narrowing to `never` when the input is `number`.
|
||||
*/
|
||||
export type NegativeInfinity = NegativeNumber & Brand<'__negativeInfinityBrand'>;
|
||||
|
||||
/**
|
||||
A safe integer (within the range of `Number.MIN_SAFE_INTEGER` to `Number.MAX_SAFE_INTEGER`).
|
||||
|
||||
Branded to prevent false-branch narrowing to `never` when the input is `number`.
|
||||
*/
|
||||
export type SafeInteger = Integer & Brand<'__safeIntegerBrand'>;
|
||||
|
||||
/**
|
||||
An even integer.
|
||||
|
||||
Branded to prevent false-branch narrowing to `never` when the input is `number`.
|
||||
*/
|
||||
export type EvenInteger = Integer & Brand<'__evenIntegerBrand'>;
|
||||
|
||||
/**
|
||||
An odd integer.
|
||||
|
||||
Branded to prevent false-branch narrowing to `never` when the input is `number`.
|
||||
*/
|
||||
export type OddInteger = Integer & Brand<'__oddIntegerBrand'>;
|
||||
|
||||
/**
|
||||
A non-negative safe integer, suitable as an array or string length.
|
||||
|
||||
Branded to prevent false-branch narrowing to `never` when the input is `number`.
|
||||
*/
|
||||
export type ValidLength = SafeInteger & NonNegativeInteger & Brand<'__validLengthBrand'>;
|
||||
3
source/utilities.ts
Normal file
3
source/utilities.ts
Normal file
|
|
@ -0,0 +1,3 @@
|
|||
export function keysOf<T extends Record<PropertyKey, unknown>>(value: T): Array<keyof T> {
|
||||
return Object.keys(value) as Array<keyof T>; // eslint-disable-line @typescript-eslint/no-unnecessary-type-assertion
|
||||
}
|
||||
2822
test/test.ts
Normal file
2822
test/test.ts
Normal file
File diff suppressed because it is too large
Load diff
12
test/tsconfig.json
Normal file
12
test/tsconfig.json
Normal file
|
|
@ -0,0 +1,12 @@
|
|||
{
|
||||
"extends": "../tsconfig.json",
|
||||
"compilerOptions": {
|
||||
"rootDir": "..",
|
||||
"noUnusedLocals": false,
|
||||
"noUnusedParameters": false
|
||||
},
|
||||
"include": [
|
||||
"../source",
|
||||
"type-tests.ts"
|
||||
]
|
||||
}
|
||||
405
test/type-tests.ts
Normal file
405
test/type-tests.ts
Normal file
|
|
@ -0,0 +1,405 @@
|
|||
import {expectTypeOf} from 'expect-type';
|
||||
import is, {
|
||||
assert as isAssert,
|
||||
assertNotNullOrUndefined,
|
||||
assertNotPrimitive,
|
||||
assertNotString,
|
||||
assertNotUndefined,
|
||||
type EvenInteger,
|
||||
type FiniteNumber,
|
||||
type Integer,
|
||||
type NaN as NaNType,
|
||||
type NegativeInfinity,
|
||||
type NegativeInteger,
|
||||
type NegativeNumber,
|
||||
type NonNegativeInteger,
|
||||
type NonNegativeNumber,
|
||||
type OddInteger,
|
||||
type PositiveInfinity,
|
||||
type PositiveInteger,
|
||||
type PositiveNumber,
|
||||
type Primitive,
|
||||
type SafeInteger,
|
||||
type ValidLength,
|
||||
} from '../source/index.ts';
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/no-restricted-types
|
||||
type UnknownNotPrimitive<Forbidden extends Primitive> = Exclude<Primitive, Forbidden> | object;
|
||||
|
||||
// For each predicate, verify two things:
|
||||
// 1. True branch narrows to the branded type.
|
||||
// 2. False branch on a `number` input stays `number` (not `never`).
|
||||
// Without the branded types, `Exclude<number, number>` = `never` would break
|
||||
// the common validation-guard pattern: if (!is.X(n)) throw; use(n).
|
||||
|
||||
const nanCheck = (value: number) => {
|
||||
if (is.nan(value)) {
|
||||
const _: NaNType = value;
|
||||
} else {
|
||||
const _: number = value;
|
||||
}
|
||||
};
|
||||
|
||||
const finiteNumberCheck = (value: number) => {
|
||||
if (is.finiteNumber(value)) {
|
||||
const _: FiniteNumber = value;
|
||||
} else {
|
||||
const _: number = value;
|
||||
}
|
||||
};
|
||||
|
||||
const nonNegativeNumberCheck = (value: number) => {
|
||||
if (is.nonNegativeNumber(value)) {
|
||||
const _: NonNegativeNumber = value;
|
||||
} else {
|
||||
const _: number = value;
|
||||
}
|
||||
};
|
||||
|
||||
const positiveIntegerCheck = (value: number) => {
|
||||
if (is.positiveInteger(value)) {
|
||||
const _: PositiveInteger = value;
|
||||
const __: Integer = value;
|
||||
const ___: NonNegativeInteger = value;
|
||||
} else {
|
||||
const _: number = value;
|
||||
}
|
||||
};
|
||||
|
||||
const negativeIntegerCheck = (value: number) => {
|
||||
if (is.negativeInteger(value)) {
|
||||
const _: NegativeInteger = value;
|
||||
const __: Integer = value;
|
||||
} else {
|
||||
const _: number = value;
|
||||
}
|
||||
};
|
||||
|
||||
const nonNegativeIntegerCheck = (value: number) => {
|
||||
if (is.nonNegativeInteger(value)) {
|
||||
const _: NonNegativeInteger = value;
|
||||
const __: Integer = value;
|
||||
} else {
|
||||
const _: number = value;
|
||||
}
|
||||
};
|
||||
|
||||
const infiniteCheck = (value: number) => {
|
||||
if (is.infinite(value)) {
|
||||
const _: PositiveInfinity | NegativeInfinity = value;
|
||||
const __: PositiveNumber | NegativeNumber = value;
|
||||
} else {
|
||||
const _: number = value;
|
||||
}
|
||||
};
|
||||
|
||||
const integerCheck = (value: number) => {
|
||||
if (is.integer(value)) {
|
||||
const _: Integer = value;
|
||||
const __: FiniteNumber = value;
|
||||
} else {
|
||||
const _: number = value;
|
||||
}
|
||||
};
|
||||
|
||||
const safeIntegerCheck = (value: number) => {
|
||||
if (is.safeInteger(value)) {
|
||||
const _: SafeInteger = value;
|
||||
const __: Integer = value;
|
||||
} else {
|
||||
const _: number = value;
|
||||
}
|
||||
};
|
||||
|
||||
const evenIntegerCheck = (value: number) => {
|
||||
if (is.evenInteger(value)) {
|
||||
const _: EvenInteger = value;
|
||||
const __: Integer = value;
|
||||
} else {
|
||||
const _: number = value;
|
||||
}
|
||||
};
|
||||
|
||||
const oddIntegerCheck = (value: number) => {
|
||||
if (is.oddInteger(value)) {
|
||||
const _: OddInteger = value;
|
||||
const __: Integer = value;
|
||||
} else {
|
||||
const _: number = value;
|
||||
}
|
||||
};
|
||||
|
||||
const positiveNumberCheck = (value: number) => {
|
||||
if (is.positiveNumber(value)) {
|
||||
const _: PositiveNumber = value;
|
||||
const __: NonNegativeNumber = value;
|
||||
} else {
|
||||
const _: number = value;
|
||||
}
|
||||
};
|
||||
|
||||
const negativeNumberCheck = (value: number) => {
|
||||
if (is.negativeNumber(value)) {
|
||||
const _: NegativeNumber = value;
|
||||
} else {
|
||||
const _: number = value;
|
||||
}
|
||||
};
|
||||
|
||||
const validLengthCheck = (value: number) => {
|
||||
if (is.validLength(value)) {
|
||||
const _: ValidLength = value;
|
||||
const __: SafeInteger = value;
|
||||
const ___: NonNegativeInteger = value;
|
||||
} else {
|
||||
const _: number = value;
|
||||
}
|
||||
};
|
||||
|
||||
const integerUnknownCheck = (value: unknown) => {
|
||||
if (is.integer(value)) {
|
||||
const _: Integer = value;
|
||||
const __: FiniteNumber = value;
|
||||
}
|
||||
};
|
||||
|
||||
const positiveIntegerUnknownCheck = (value: unknown) => {
|
||||
if (is.positiveInteger(value)) {
|
||||
const _: PositiveInteger = value;
|
||||
const __: NonNegativeInteger = value;
|
||||
}
|
||||
};
|
||||
|
||||
const integerMixedUnionCheck = (value: string | number) => {
|
||||
if (is.integer(value)) {
|
||||
const _: number = value;
|
||||
} else {
|
||||
const _: string = value;
|
||||
}
|
||||
};
|
||||
|
||||
const positiveNumberMixedUnionCheck = (value: string | number) => {
|
||||
if (is.positiveNumber(value)) {
|
||||
const _: number = value;
|
||||
} else {
|
||||
const _: string = value;
|
||||
}
|
||||
};
|
||||
|
||||
const chainedNumericGuardCheck = (value: number) => {
|
||||
if (is.positiveNumber(value) && is.integer(value)) {
|
||||
const _: PositiveNumber = value;
|
||||
const __: Integer = value;
|
||||
const ___: FiniteNumber = value;
|
||||
}
|
||||
};
|
||||
|
||||
const distinctNumericBrandsStayDistinct = (
|
||||
positiveInteger: PositiveInteger,
|
||||
negativeInteger: NegativeInteger,
|
||||
validLength: ValidLength,
|
||||
) => {
|
||||
// @ts-expect-error -- Distinct numeric refinements must not collapse into each other.
|
||||
const _: NegativeInteger = positiveInteger;
|
||||
// @ts-expect-error -- ValidLength is non-negative and must not become a signed integer refinement.
|
||||
const __: NegativeInteger = validLength;
|
||||
|
||||
return negativeInteger;
|
||||
};
|
||||
|
||||
const assertNotUndefinedCheck = (value: string | undefined) => {
|
||||
isAssert.not.undefined(value);
|
||||
expectTypeOf(value).toEqualTypeOf<string>();
|
||||
};
|
||||
|
||||
const assertNotUndefinedUnknownCheck = (value: unknown) => {
|
||||
isAssert.not.undefined(value);
|
||||
expectTypeOf(value).toEqualTypeOf<UnknownNotPrimitive<undefined>>();
|
||||
};
|
||||
|
||||
const assertNotUndefinedGenericCheck = <T>(value: T) => {
|
||||
isAssert.not.undefined(value);
|
||||
const _: Exclude<T, undefined> = value;
|
||||
};
|
||||
|
||||
const nullValue = null;
|
||||
type Null = typeof nullValue;
|
||||
|
||||
const assertNotNullUnknownCheck = (value: unknown) => {
|
||||
isAssert.not.null(value);
|
||||
expectTypeOf(value).toEqualTypeOf<UnknownNotPrimitive<Null>>();
|
||||
};
|
||||
|
||||
const assertNotNullOrUndefinedCheck = (value: string | Null | undefined) => {
|
||||
isAssert.not.nullOrUndefined(value);
|
||||
expectTypeOf(value).toEqualTypeOf<string>();
|
||||
};
|
||||
|
||||
const assertNotNullOrUndefinedUnknownCheck = (value: unknown) => {
|
||||
isAssert.not.nullOrUndefined(value);
|
||||
expectTypeOf(value).toEqualTypeOf<UnknownNotPrimitive<Null | undefined>>();
|
||||
};
|
||||
|
||||
const assertNotStringCheck = (value: string | number) => {
|
||||
isAssert.not.string(value);
|
||||
expectTypeOf(value).toEqualTypeOf<number>();
|
||||
};
|
||||
|
||||
const assertNotStringUnknownCheck = (value: unknown) => {
|
||||
isAssert.not.string(value);
|
||||
expectTypeOf(value).toEqualTypeOf<UnknownNotPrimitive<string>>();
|
||||
};
|
||||
|
||||
const assertNotStringGenericCheck = <T>(value: T) => {
|
||||
isAssert.not.string(value);
|
||||
const _: Exclude<T, string> = value;
|
||||
};
|
||||
|
||||
const assertNotBooleanUnknownCheck = (value: unknown) => {
|
||||
isAssert.not.boolean(value);
|
||||
expectTypeOf(value).toEqualTypeOf<UnknownNotPrimitive<boolean>>();
|
||||
};
|
||||
|
||||
const assertNotSymbolUnknownCheck = (value: unknown) => {
|
||||
isAssert.not.symbol(value);
|
||||
expectTypeOf(value).toEqualTypeOf<UnknownNotPrimitive<symbol>>();
|
||||
};
|
||||
|
||||
const assertNotBigintUnknownCheck = (value: unknown) => {
|
||||
isAssert.not.bigint(value);
|
||||
expectTypeOf(value).toEqualTypeOf<UnknownNotPrimitive<bigint>>();
|
||||
};
|
||||
|
||||
const assertNotPrimitiveUnknownCheck = (value: unknown) => {
|
||||
isAssert.not.primitive(value);
|
||||
// eslint-disable-next-line @typescript-eslint/no-restricted-types
|
||||
expectTypeOf(value).toEqualTypeOf<object>();
|
||||
};
|
||||
|
||||
const assertNotPrimitiveGenericCheck = <T>(value: T) => {
|
||||
isAssert.not.primitive(value);
|
||||
const _: Exclude<T, Primitive> = value;
|
||||
};
|
||||
|
||||
const assertNotNamedUndefinedExportCheck = (value: 0 | false | '' | Null | undefined | 'ok') => {
|
||||
assertNotUndefined(value);
|
||||
expectTypeOf(value).toEqualTypeOf<0 | false | '' | Null | 'ok'>();
|
||||
};
|
||||
|
||||
const assertNotNamedNullOrUndefinedUnknownExportCheck = (value: unknown) => {
|
||||
assertNotNullOrUndefined(value);
|
||||
expectTypeOf(value).toEqualTypeOf<UnknownNotPrimitive<Null | undefined>>();
|
||||
};
|
||||
|
||||
const assertNotNamedStringExportCheck = (value: string | number) => {
|
||||
assertNotString(value);
|
||||
expectTypeOf(value).toEqualTypeOf<number>();
|
||||
};
|
||||
|
||||
const assertNotNamedStringUnknownExportCheck = (value: unknown) => {
|
||||
assertNotString(value);
|
||||
expectTypeOf(value).toEqualTypeOf<UnknownNotPrimitive<string>>();
|
||||
};
|
||||
|
||||
const assertNotNamedPrimitiveUnknownExportCheck = (value: unknown) => {
|
||||
assertNotPrimitive(value);
|
||||
// eslint-disable-next-line @typescript-eslint/no-restricted-types
|
||||
expectTypeOf(value).toEqualTypeOf<object>();
|
||||
};
|
||||
|
||||
const assertNotCallableDoesNotExistCheck = (value: string | undefined) => {
|
||||
// @ts-expect-error -- Generic negative assertions cannot safely infer complement types from arbitrary predicates.
|
||||
isAssert.not(is.undefined, value);
|
||||
const _: string | undefined = value;
|
||||
};
|
||||
|
||||
const assertNotNumberDoesNotExistCheck = (value: string | number) => {
|
||||
// @ts-expect-error -- `is.number` rejects `NaN`, so a narrowing negative assertion would be unsound.
|
||||
isAssert.not.number(value); // eslint-disable-line @typescript-eslint/no-unsafe-call
|
||||
const _: string | number = value;
|
||||
};
|
||||
|
||||
const assertNotIntegerDoesNotExistCheck = (value: string | number) => {
|
||||
// @ts-expect-error -- Numeric refinements are intentionally excluded from `assert.not`.
|
||||
isAssert.not.integer(value); // eslint-disable-line @typescript-eslint/no-unsafe-call
|
||||
const _: string | number = value;
|
||||
};
|
||||
|
||||
const assertNotObjectDoesNotExistCheck = (value: Record<string, unknown> | string) => {
|
||||
// @ts-expect-error -- TypeScript's `{}` type includes primitives, so `not.object` cannot safely narrow every object-like input.
|
||||
isAssert.not.object(value); // eslint-disable-line @typescript-eslint/no-unsafe-call
|
||||
const _: Record<string, unknown> | string = value;
|
||||
};
|
||||
|
||||
const assertNotBlobDoesNotExistCheck = (value: Blob | File | string) => {
|
||||
// @ts-expect-error -- `File` extends `Blob` in TypeScript but does not match the exact runtime `Blob` check.
|
||||
isAssert.not.blob(value); // eslint-disable-line @typescript-eslint/no-unsafe-call
|
||||
const _: Blob | File | string = value;
|
||||
};
|
||||
|
||||
const assertNotMapDoesNotExistCheck = (value: Map<string, number> | string) => {
|
||||
// @ts-expect-error -- Structural object types such as `Map` can be assignable in TypeScript without matching the runtime brand check.
|
||||
isAssert.not.map(value); // eslint-disable-line @typescript-eslint/no-unsafe-call, unicorn/no-array-callback-reference
|
||||
const _: Map<string, number> | string = value;
|
||||
};
|
||||
|
||||
const assertNotSetDoesNotExistCheck = (value: Set<string> | string) => {
|
||||
// @ts-expect-error -- Structural object types such as `Set` can be assignable in TypeScript without matching the runtime brand check.
|
||||
isAssert.not.set(value); // eslint-disable-line @typescript-eslint/no-unsafe-call
|
||||
const _: Set<string> | string = value;
|
||||
};
|
||||
|
||||
const assertNotDateDoesNotExistCheck = (value: Date | string) => {
|
||||
// @ts-expect-error -- Structural object types such as `Date` can be assignable in TypeScript without matching the runtime brand check.
|
||||
isAssert.not.date(value); // eslint-disable-line @typescript-eslint/no-unsafe-call
|
||||
const _: Date | string = value;
|
||||
};
|
||||
|
||||
// Suppress unused variable warnings
|
||||
nanCheck(42);
|
||||
finiteNumberCheck(42);
|
||||
nonNegativeNumberCheck(42);
|
||||
positiveIntegerCheck(42);
|
||||
negativeIntegerCheck(-1);
|
||||
nonNegativeIntegerCheck(0);
|
||||
infiniteCheck(Number.POSITIVE_INFINITY);
|
||||
integerCheck(1);
|
||||
safeIntegerCheck(1);
|
||||
evenIntegerCheck(2);
|
||||
oddIntegerCheck(1);
|
||||
positiveNumberCheck(1);
|
||||
negativeNumberCheck(-1);
|
||||
validLengthCheck(0);
|
||||
integerUnknownCheck(1);
|
||||
positiveIntegerUnknownCheck(1);
|
||||
integerMixedUnionCheck(1);
|
||||
positiveNumberMixedUnionCheck(1);
|
||||
chainedNumericGuardCheck(1);
|
||||
distinctNumericBrandsStayDistinct(42 as PositiveInteger, -1 as NegativeInteger, 0 as ValidLength);
|
||||
assertNotUndefinedCheck('🦄');
|
||||
assertNotUndefinedUnknownCheck('🦄');
|
||||
assertNotUndefinedGenericCheck<string | undefined>('🦄');
|
||||
assertNotNullUnknownCheck('🦄');
|
||||
assertNotNullOrUndefinedCheck('🦄');
|
||||
assertNotNullOrUndefinedUnknownCheck('🦄');
|
||||
assertNotStringCheck(1);
|
||||
assertNotStringUnknownCheck(1);
|
||||
assertNotStringGenericCheck<string | number>(1);
|
||||
assertNotBooleanUnknownCheck(1);
|
||||
assertNotSymbolUnknownCheck(1);
|
||||
assertNotBigintUnknownCheck(1);
|
||||
assertNotPrimitiveUnknownCheck({});
|
||||
assertNotPrimitiveGenericCheck<string | {unicorn: true}>({unicorn: true});
|
||||
assertNotNamedUndefinedExportCheck(0);
|
||||
assertNotNamedNullOrUndefinedUnknownExportCheck('🦄');
|
||||
assertNotNamedStringExportCheck(1);
|
||||
assertNotNamedStringUnknownExportCheck(1);
|
||||
assertNotNamedPrimitiveUnknownExportCheck({});
|
||||
assertNotCallableDoesNotExistCheck('🦄');
|
||||
assertNotNumberDoesNotExistCheck(Number.NaN);
|
||||
assertNotIntegerDoesNotExistCheck(1.5);
|
||||
assertNotObjectDoesNotExistCheck('🦄');
|
||||
assertNotBlobDoesNotExistCheck('🦄');
|
||||
assertNotMapDoesNotExistCheck('🦄');
|
||||
assertNotSetDoesNotExistCheck('🦄');
|
||||
assertNotDateDoesNotExistCheck('🦄');
|
||||
|
|
@ -1,28 +1,12 @@
|
|||
{
|
||||
"extends": "@sindresorhus/tsconfig",
|
||||
"compilerOptions": {
|
||||
"outDir": "dist",
|
||||
"target": "es2015",
|
||||
"module": "commonjs",
|
||||
"moduleResolution": "node",
|
||||
"declaration": true,
|
||||
"skipLibCheck": true,
|
||||
"pretty": true,
|
||||
"newLine": "lf",
|
||||
"lib": [
|
||||
"dom",
|
||||
"es2015",
|
||||
"es2017.sharedmemory"
|
||||
],
|
||||
"stripInternal": true,
|
||||
"strict": true,
|
||||
"noImplicitReturns": true,
|
||||
"noUnusedLocals": true,
|
||||
"noUnusedParameters": true,
|
||||
"noFallthroughCasesInSwitch": true,
|
||||
"esModuleInterop": true
|
||||
"types": ["node"],
|
||||
"rootDir": "source",
|
||||
"allowImportingTsExtensions": true,
|
||||
"rewriteRelativeImportExtensions": true
|
||||
},
|
||||
"exclude": [
|
||||
"node_modules",
|
||||
"dist"
|
||||
]
|
||||
"include": [
|
||||
"source"
|
||||
],
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,3 +0,0 @@
|
|||
{
|
||||
"extends": "tslint-xo"
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue