├── .gitignore ├── .node-version ├── .npm-version ├── .npmignore ├── .prettierrc ├── LICENSE ├── README.md ├── coerce.d.ts ├── coerce.js ├── kame-loader.js ├── package-lock.json ├── package.json ├── src ├── api-functions.ts ├── basic-types.ts ├── coerce.test.ts ├── coerce │ ├── api-functions.ts │ ├── coerce.ts │ └── index.ts ├── index.test.ts ├── index.ts ├── type-constructors.ts ├── type-validator.ts └── utils.ts └── tsconfig.json /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | .DS_Store 3 | dist 4 | -------------------------------------------------------------------------------- /.node-version: -------------------------------------------------------------------------------- 1 | v22.13.0 2 | -------------------------------------------------------------------------------- /.npm-version: -------------------------------------------------------------------------------- 1 | 10.9.2 2 | -------------------------------------------------------------------------------- /.npmignore: -------------------------------------------------------------------------------- 1 | src 2 | .prettierrc 3 | .node-version 4 | .npm-version 5 | kame-loader.js 6 | -------------------------------------------------------------------------------- /.prettierrc: -------------------------------------------------------------------------------- 1 | {} 2 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License Copyright (c) 2022-2023 Lily Skye 2 | 3 | Permission is hereby granted, free of 4 | charge, to any person obtaining a copy of this software and associated 5 | documentation files (the "Software"), to deal in the Software without 6 | restriction, including without limitation the rights to use, copy, modify, merge, 7 | publish, distribute, sublicense, and/or sell copies of the Software, and to 8 | permit persons to whom the Software is furnished to do so, subject to the 9 | following conditions: 10 | 11 | The above copyright notice and this permission notice 12 | (including the next paragraph) shall be included in all copies or substantial 13 | portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF 16 | ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 17 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO 18 | EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR 19 | OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 20 | FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 21 | THE SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # pheno 2 | 3 | Simple, lightweight at-runtime type checking functions, with full TypeScript support 4 | 5 | ## Features 6 | 7 | - Full TypeScript integration: TypeScript understands that `assertType` and `isOfType` narrow the types of things, and will refine them accordingly 8 | - Simple: Type validators are just functions that return booleans. 9 | - Effective: There's lots of utility functions that help you represent all the various types you'd care about in your code. 10 | - Lightweight: The whole library is 18K minified (4.5K gzipped, 4.1K brotli-ified). 11 | 12 | ## Usage Example 13 | 14 | ```ts 15 | import * as types from "pheno"; 16 | 17 | function something(first: unknown, second: unknown) { 18 | // Throws an error if `first` is not a string 19 | types.assertType(first, types.string); 20 | 21 | // Throws an error if `second` is not `string | number` 22 | types.assertType(second, types.union(types.string, types.number)); 23 | 24 | // Typescript now knows that `first` is a string and `second` is `string | number` 25 | return first + " " + String(second); 26 | } 27 | ``` 28 | 29 | ## List of types and type builder functions 30 | 31 | - `and` 32 | - `any` 33 | - `anyArray` 34 | - `anyFunction` 35 | - `anyMap` 36 | - `anyObject` 37 | - `anySet` 38 | - `anyTypeValidator` 39 | - `array` (alias of `arrayOfUnknown`) 40 | - `arrayOf` 41 | - `arrayOfAny` 42 | - `arrayOfUnknown` 43 | - `Array` (alias of `arrayOfUnknown`) 44 | - `assertType` (assert that a value has the provided type, and throw an error message if it doesn't) 45 | - `asType` (do a TypeScript `as` cast to convert the value into the provided type) 46 | - `bigint` 47 | - `BigInt` (alias of `bigint`) 48 | - `boolean` 49 | - `Boolean` (alias of `boolean`) 50 | - `Date` 51 | - `Error` 52 | - `exactBigInt` 53 | - `exactNumber` 54 | - `exactString` 55 | - `exactSymbol` 56 | - `false` 57 | - `falsy` 58 | - `Function` (alias of `unknownFunction`) 59 | - `hasClassName` 60 | - `hasToStringTag` 61 | - `Infinity` 62 | - `instanceOf` 63 | - `integer` 64 | - `intersection` 65 | - `isOfType` (return a boolean indicating if a value is of the provided type) 66 | - `map` (alias of `unknownMap`) 67 | - `Map` (alias of `unknownMap`) 68 | - `mapOf` 69 | - `mappingObjectOf` 70 | - `maybe` 71 | - `NaN` 72 | - `NegativeInfinity` 73 | - `never` 74 | - `nonNullOrUndefined` 75 | - `null` 76 | - `nullish` 77 | - `void` (alias of `nullish`) 78 | - `number` (doesn't include NaN, Infinity, or -Infinity) 79 | - `Number` (alias of `number`) 80 | - `numberIncludingNanAndInfinities` 81 | - `object` (alias of `unknownObject`) 82 | - `Object` (alias of `unknownObject`) 83 | - `objectOrNull` 84 | - `objectWithOnlyTheseProperties` 85 | - `objectWithProperties` 86 | - `or` 87 | - `optional` 88 | - `partialObjectWithProperties` 89 | - `record` 90 | - `RegExp` 91 | - `set` (alias of `unknownSet`) 92 | - `Set` (alias of `unknownSet`) 93 | - `setOf` 94 | - `string` 95 | - `String` (alias of `string`) 96 | - `stringifyValue` (safe and lightweight value-to-string function, for printing values in error messages) 97 | - `stringMatching` 98 | - `Symbol` 99 | - `symbol` (alias of `Symbol`) 100 | - `symbolFor` 101 | - `true` 102 | - `truthy` 103 | - `tuple` 104 | - `undefined` 105 | - `union` 106 | - `unknown` 107 | - `unknownFunction` 108 | - `unknownMap` 109 | - `unknownObject` 110 | - `unknownSet` 111 | - `unknownTypeValidator` 112 | - `ArrayBuffer` 113 | - `SharedArrayBuffer` 114 | - `DataView` 115 | - `TypedArray` 116 | - `Int8Array` 117 | - `Uint8Array` 118 | - `Uint8ClampedArray` 119 | - `Int16Array` 120 | - `Uint16Array` 121 | - `Int32Array` 122 | - `Uint32Array` 123 | - `Float32Array` 124 | - `Float64Array` 125 | 126 | Please see the TypeScript types for each of these in either your editor's autocomplete or pheno's source code for more information. 127 | 128 | ## License 129 | 130 | MIT 131 | -------------------------------------------------------------------------------- /coerce.d.ts: -------------------------------------------------------------------------------- 1 | export * from "./dist/coerce"; 2 | import coerce from "./dist/coerce"; 3 | export default coerce; 4 | -------------------------------------------------------------------------------- /coerce.js: -------------------------------------------------------------------------------- 1 | const coerce = require("./dist/coerce"); 2 | module.exports = Object.assign(coerce.default, coerce); 3 | Object.defineProperty(module.exports, "__esModule", { value: true }); 4 | -------------------------------------------------------------------------------- /kame-loader.js: -------------------------------------------------------------------------------- 1 | const { defaultLoader } = require("kame"); 2 | 3 | exports.load = function load(filename) { 4 | return defaultLoader.load(filename, { target: "es5" }); 5 | }; 6 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "pheno", 3 | "version": "0.12.0", 4 | "description": "Simple, lightweight at-runtime type checking functions, with full TypeScript support", 5 | "main": "dist/bundle.js", 6 | "types": "dist/index.d.ts", 7 | "browser": "dist/bundle.min.js", 8 | "scripts": { 9 | "build:ts": "tsc", 10 | "build:kame": "kame bundle --output ./dist/bundle.js --loader ./kame-loader.js", 11 | "build:minify": "terser ./dist/bundle.js -m eval=true -c > ./dist/bundle.min.js", 12 | "build": "rm -rf dist && npm run build:ts && npm run build:kame && npm run build:minify", 13 | "build:watch": "chokidar 'src/**/*' 'kame-loader.js' -c 'npm run build && echo Done' --initial", 14 | "test": "vitest" 15 | }, 16 | "keywords": [ 17 | "type", 18 | "check", 19 | "runtime", 20 | "validate", 21 | "test", 22 | "assert", 23 | "typescript", 24 | "is", 25 | "asserts" 26 | ], 27 | "author": "Lily Skye ", 28 | "license": "MIT", 29 | "devDependencies": { 30 | "chokidar-cli": "^3.0.0", 31 | "kame": "^0.14.0", 32 | "prettier": "^3.4.2", 33 | "terser": "^5.37.0", 34 | "typescript": "^5.7.3", 35 | "vitest": "^3.0.2" 36 | }, 37 | "repository": { 38 | "type": "git", 39 | "url": "git+https://github.com/suchipi/pheno.git" 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /src/api-functions.ts: -------------------------------------------------------------------------------- 1 | import type { TypeValidator } from "./type-validator"; 2 | 3 | /** 4 | * Simple value-to-string converter, using JSON.stringify with a custom 5 | * replacer and a try-catch around it. Kinda like a lightweight stand-in for 6 | * an "inspect" function. assertType's default messageMaker uses this function. 7 | */ 8 | export function stringifyValue(value: any) { 9 | try { 10 | return JSON.stringify(value, replacer); 11 | } catch (err) { 12 | return JSON.stringify({ 13 | [`${String(value)} that failed to serialize due to error`]: String(err), 14 | }); 15 | } 16 | } 17 | 18 | function replacer(_key: string, value: any) { 19 | if (value === null) return value; 20 | if (Array.isArray(value)) return value; 21 | 22 | if (Number.isNaN(value) || value === Infinity || value === -Infinity) { 23 | return `<${String(value)}>`; 24 | } 25 | 26 | switch (typeof value) { 27 | case "boolean": 28 | case "string": 29 | case "number": { 30 | return value; 31 | } 32 | 33 | case "undefined": { 34 | return ""; 35 | } 36 | 37 | case "function": { 38 | return ``; 39 | } 40 | 41 | case "bigint": { 42 | return ``; 43 | } 44 | 45 | case "symbol": { 46 | return ``; 47 | } 48 | } 49 | 50 | const tag = 51 | typeof value.constructor === "function" 52 | ? value.constructor.name || "anonymous constructor" 53 | : "Object"; 54 | 55 | if (tag === "Object") { 56 | return value; 57 | } 58 | 59 | if (tag === "Map") { 60 | return { 61 | [`<${tag} of size ${value.size}>`]: Array.from(value.entries()), 62 | }; 63 | } else if (tag === "Set") { 64 | return { 65 | [`<${tag} of size ${value.size}>`]: Array.from(value.values()), 66 | }; 67 | } else { 68 | return `<${tag}>`; 69 | } 70 | } 71 | 72 | const defaultMessageMaker = (target: any, expectedType: TypeValidator) => 73 | `Expected value of type ${ 74 | typeof expectedType === "function" 75 | ? expectedType.name || "" 76 | : `` 77 | }, but received ${stringifyValue(target)}`; 78 | 79 | function _assertType( 80 | target: any, 81 | type: TypeValidator, 82 | messageMaker: ( 83 | target: any, 84 | expectedType: TypeValidator, 85 | ) => string = defaultMessageMaker, 86 | ErrorConstructor: { new (message?: string): any } = TypeError, 87 | ): asserts target is T { 88 | if (typeof type !== "function") { 89 | throw new TypeError( 90 | `'type' argument passed into 'assertType' was the wrong type. It should be a function, but it was: ${stringifyValue( 91 | type, 92 | )}`, 93 | ); 94 | } 95 | 96 | const isOfType = type(target); 97 | if (!isOfType) { 98 | throw new ErrorConstructor(messageMaker(target, type)); 99 | } 100 | } 101 | 102 | Object.defineProperties(_assertType, { 103 | name: { value: "assertType", configurable: true }, 104 | defaultMessageMaker: { 105 | get() { 106 | return defaultMessageMaker; 107 | }, 108 | }, 109 | }); 110 | 111 | export const assertType: { 112 | readonly defaultMessageMaker: typeof defaultMessageMaker; 113 | } & typeof _assertType = _assertType as any; 114 | 115 | export function isOfType(target: any, type: TypeValidator): target is T { 116 | if (typeof type !== "function") { 117 | throw new TypeError( 118 | `'type' argument passed into 'isOfType' was the wrong type. It should be a function, but it was: ${stringifyValue( 119 | type, 120 | )}`, 121 | ); 122 | } 123 | 124 | return type(target); 125 | } 126 | 127 | const any: any = null; 128 | export function asType(target: unknown, type: TypeValidator = any): T { 129 | return target as any; 130 | } 131 | -------------------------------------------------------------------------------- /src/basic-types.ts: -------------------------------------------------------------------------------- 1 | import type { TypeValidator } from "./type-validator"; 2 | import { setName, isTagged } from "./utils"; 3 | 4 | export const any: TypeValidator = (_value): _value is any => true; 5 | setName(any, "any"); 6 | 7 | export const unknown: TypeValidator = (_value): _value is unknown => 8 | true; 9 | setName(any, "unknown"); 10 | 11 | export const anyObject: TypeValidator<{ 12 | [key: string | number | symbol]: any; 13 | }> = (target: any): target is any => 14 | typeof target === "object" && target != null; 15 | setName(anyObject, "anyObject"); 16 | 17 | export const unknownObject: TypeValidator<{}> = (target: any): target is any => 18 | anyObject(target); 19 | setName(unknownObject, "unknownObject"); 20 | 21 | export const object: TypeValidator<{}> = (target: any): target is any => 22 | anyObject(target); 23 | setName(object, "object"); 24 | 25 | const Object_ = object; 26 | export { Object_ as Object }; 27 | 28 | export const objectOrNull: TypeValidator< 29 | { 30 | [key: string | number | symbol]: any; 31 | } | null 32 | > = (target: any): target is any => typeof target === "object"; 33 | setName(objectOrNull, "objectOrNull"); 34 | 35 | export const arrayOfAny: TypeValidator> = (target): target is any => 36 | Array.isArray(target); 37 | setName(arrayOfAny, "arrayOfAny"); 38 | 39 | export const arrayOfUnknown: TypeValidator> = ( 40 | target, 41 | ): target is any => arrayOfAny(target); 42 | setName(arrayOfUnknown, "arrayOfUnknown"); 43 | 44 | export const array: TypeValidator> = (target): target is any => 45 | arrayOfAny(target); 46 | setName(array, "array"); 47 | 48 | const Array_ = array; 49 | export { Array_ as Array }; 50 | 51 | export const anyArray: TypeValidator> = (target): target is any => 52 | arrayOfAny(target); 53 | setName(anyArray, "anyArray"); 54 | 55 | export const boolean: TypeValidator = (target): target is boolean => 56 | typeof target === "boolean"; 57 | setName(boolean, "boolean"); 58 | 59 | const Boolean_ = boolean; 60 | export { Boolean_ as Boolean }; 61 | 62 | export const string: TypeValidator = (target): target is string => 63 | typeof target === "string"; 64 | setName(string, "string"); 65 | 66 | const String_ = string; 67 | export { String_ as String }; 68 | 69 | const null_: TypeValidator = (target): target is null => target === null; 70 | setName(null_, "null"); 71 | 72 | export { null_ as null }; 73 | 74 | const undefined_: TypeValidator = (target): target is undefined => 75 | typeof target === "undefined"; 76 | setName(undefined_, "undefined"); 77 | 78 | export { undefined_ as undefined }; 79 | 80 | export const nullish: TypeValidator = ( 81 | target, 82 | ): target is null | undefined => target == null; 83 | setName(nullish, "nullish"); 84 | 85 | const void_ = nullish; 86 | export { void_ as void }; 87 | 88 | export const numberIncludingNanAndInfinities: TypeValidator = ( 89 | target, 90 | ): target is number => typeof target === "number"; 91 | setName(numberIncludingNanAndInfinities, "numberIncludingNanAndInfinities"); 92 | 93 | export const number: TypeValidator = (target): target is number => 94 | typeof target === "number" && 95 | !Number.isNaN(target) && 96 | !Infinity_(target) && 97 | !NegativeInfinity(target); 98 | setName(number, "number"); 99 | 100 | const Number_ = number; 101 | export { Number_ as Number }; 102 | 103 | const NaN_: TypeValidator = (target): target is number => 104 | Number.isNaN(target); 105 | setName(NaN_, "NaN"); 106 | 107 | export { NaN_ as NaN }; 108 | 109 | const Infinity_: TypeValidator = (target): target is number => 110 | target === Infinity; 111 | setName(Infinity_, "Infinity"); 112 | 113 | export { Infinity_ as Infinity }; 114 | 115 | export const NegativeInfinity: TypeValidator = ( 116 | target, 117 | ): target is number => target === -Infinity; 118 | setName(NegativeInfinity, "NegativeInfinity"); 119 | 120 | export const integer: TypeValidator = (target): target is number => 121 | number(target) && target % 1 === 0; 122 | setName(integer, "integer"); 123 | 124 | const bigint_: TypeValidator = (target): target is bigint => 125 | typeof target === "bigint"; 126 | setName(bigint_, "bigint"); 127 | 128 | export { bigint_ as bigint, bigint_ as BigInt }; 129 | 130 | export const never: TypeValidator = (_target): _target is never => false; 131 | setName(never, "never"); 132 | 133 | export const anyFunction: TypeValidator<(...args: any) => any> = ( 134 | target, 135 | ): target is (...args: any) => any => typeof target === "function"; 136 | setName(anyFunction, "anyFunction"); 137 | 138 | export const unknownFunction: TypeValidator< 139 | (...args: Array) => unknown 140 | > = (target): target is (...args: Array) => unknown => 141 | anyFunction(target); 142 | setName(unknownFunction, "unknownFunction"); 143 | 144 | const Function_ = unknownFunction; 145 | export { Function_ as Function }; 146 | 147 | const false_: TypeValidator = (target): target is false => 148 | target === false; 149 | setName(false_, "false"); 150 | 151 | export { false_ as false }; 152 | 153 | const true_: TypeValidator = (target): target is true => target === true; 154 | setName(true_, "true"); 155 | 156 | export { true_ as true }; 157 | 158 | export const falsy: TypeValidator = ( 159 | target, 160 | ): target is false | null | undefined | "" | 0 => !Boolean(target); 161 | setName(falsy, "falsy"); 162 | 163 | export const truthy = ( 164 | target: T | false | null | undefined | "" | 0, 165 | ): target is T => Boolean(target); 166 | setName(truthy, "truthy"); 167 | 168 | export const nonNullOrUndefined = ( 169 | target: T | null | undefined, 170 | ): target is T => target != null; 171 | setName(nonNullOrUndefined, "nonNullOrUndefined"); 172 | 173 | const Error_: TypeValidator = (target): target is Error => 174 | anyObject(target) && 175 | string(target.name) && 176 | string(target.message) && 177 | string(target.stack); 178 | setName(Error_, "Error"); 179 | 180 | export { Error_ as Error }; 181 | 182 | const Symbol_: TypeValidator = (target): target is symbol => 183 | typeof target === "symbol"; 184 | setName(Symbol_, "Symbol"); 185 | 186 | export { Symbol_ as Symbol, Symbol_ as symbol }; 187 | 188 | const RegExp_: TypeValidator = (target): target is RegExp => 189 | target instanceof RegExp || isTagged(target, "RegExp"); 190 | setName(RegExp_, "RegExp"); 191 | 192 | export { RegExp_ as RegExp }; 193 | 194 | const Date_: TypeValidator = (target): target is Date => 195 | target instanceof Date || isTagged(target, "Date"); 196 | setName(Date_, "Date"); 197 | 198 | export { Date_ as Date }; 199 | 200 | export const anyMap: TypeValidator> = ( 201 | target, 202 | ): target is Map => 203 | target instanceof Map || 204 | (anyObject(target) && 205 | anyFunction(target.constructor) && 206 | target.constructor.name === "Map" && 207 | anyFunction(target.entries) && 208 | number(target.size)); 209 | setName(anyMap, "anyMap"); 210 | 211 | export const unknownMap: TypeValidator> = ( 212 | target, 213 | ): target is Map => anyMap(target); 214 | setName(unknownMap, "unknownMap"); 215 | 216 | export const map: TypeValidator> = ( 217 | target, 218 | ): target is Map => anyMap(target); 219 | setName(map, "map"); 220 | 221 | export { map as Map }; 222 | 223 | export const anySet: TypeValidator> = (target): target is Set => 224 | target instanceof Set || 225 | (anyObject(target) && 226 | anyFunction(target.constructor) && 227 | target.constructor.name === "Set" && 228 | anyFunction(target.entries) && 229 | number(target.size)); 230 | setName(anySet, "anySet"); 231 | 232 | export const unknownSet: TypeValidator> = ( 233 | target, 234 | ): target is Set => anySet(target); 235 | setName(unknownSet, "unknownSet"); 236 | 237 | export const set: TypeValidator> = ( 238 | target, 239 | ): target is Set => anySet(target); 240 | setName(set, "set"); 241 | 242 | export { set as Set }; 243 | 244 | const ArrayBuffer_: TypeValidator = ( 245 | target, 246 | ): target is ArrayBuffer => { 247 | return target instanceof ArrayBuffer || isTagged(target, "ArrayBuffer"); 248 | }; 249 | setName(ArrayBuffer_, "ArrayBuffer"); 250 | export { ArrayBuffer_ as ArrayBuffer }; 251 | 252 | const SharedArrayBuffer_: TypeValidator = ( 253 | target, 254 | ): target is SharedArrayBuffer => { 255 | return ( 256 | target instanceof SharedArrayBuffer || isTagged(target, "SharedArrayBuffer") 257 | ); 258 | }; 259 | setName(SharedArrayBuffer_, "SharedArrayBuffer"); 260 | export { SharedArrayBuffer_ as SharedArrayBuffer }; 261 | 262 | const DataView_: TypeValidator = (target): target is DataView => { 263 | return target instanceof DataView || isTagged(target, "DataView"); 264 | }; 265 | setName(DataView_, "DataView"); 266 | export { DataView_ as DataView }; 267 | 268 | const TypedArray_: TypeValidator< 269 | | Int8Array 270 | | Uint8Array 271 | | Uint8ClampedArray 272 | | Int16Array 273 | | Uint16Array 274 | | Int32Array 275 | | Uint32Array 276 | | Float32Array 277 | | Float64Array 278 | > = ( 279 | target, 280 | ): target is 281 | | Int8Array 282 | | Uint8Array 283 | | Uint8ClampedArray 284 | | Int16Array 285 | | Uint16Array 286 | | Int32Array 287 | | Uint32Array 288 | | Float32Array 289 | | Float64Array => { 290 | return ArrayBuffer.isView(target) && !DataView_(target); 291 | }; 292 | setName(TypedArray_, "TypedArray"); 293 | export { TypedArray_ as TypedArray }; 294 | 295 | const Int8Array_: TypeValidator = (target): target is Int8Array => { 296 | return target instanceof Int8Array || isTagged(target, "Int8Array"); 297 | }; 298 | setName(Int8Array_, "Int8Array"); 299 | export { Int8Array_ as Int8Array }; 300 | 301 | const Uint8Array_: TypeValidator = ( 302 | target, 303 | ): target is Uint8Array => { 304 | return target instanceof Uint8Array || isTagged(target, "Uint8Array"); 305 | }; 306 | setName(Uint8Array_, "Uint8Array"); 307 | export { Uint8Array_ as Uint8Array }; 308 | 309 | const Uint8ClampedArray_: TypeValidator = ( 310 | target, 311 | ): target is Uint8ClampedArray => { 312 | return ( 313 | target instanceof Uint8ClampedArray || isTagged(target, "Uint8ClampedArray") 314 | ); 315 | }; 316 | setName(Uint8ClampedArray_, "Uint8ClampedArray"); 317 | export { Uint8ClampedArray_ as Uint8ClampedArray }; 318 | 319 | const Int16Array_: TypeValidator = ( 320 | target, 321 | ): target is Int16Array => { 322 | return target instanceof Int16Array || isTagged(target, "Int16Array"); 323 | }; 324 | setName(Int16Array_, "Int16Array"); 325 | export { Int16Array_ as Int16Array }; 326 | 327 | const Uint16Array_: TypeValidator = ( 328 | target, 329 | ): target is Uint16Array => { 330 | return target instanceof Uint16Array || isTagged(target, "Uint16Array"); 331 | }; 332 | setName(Uint16Array_, "Uint16Array"); 333 | export { Uint16Array_ as Uint16Array }; 334 | 335 | const Int32Array_: TypeValidator = ( 336 | target, 337 | ): target is Int32Array => { 338 | return target instanceof Int32Array || isTagged(target, "Int32Array"); 339 | }; 340 | setName(Int32Array_, "Int32Array"); 341 | export { Int32Array_ as Int32Array }; 342 | 343 | const Uint32Array_: TypeValidator = ( 344 | target, 345 | ): target is Uint32Array => { 346 | return target instanceof Uint32Array || isTagged(target, "Uint32Array"); 347 | }; 348 | setName(Uint32Array_, "Uint32Array"); 349 | export { Uint32Array_ as Uint32Array }; 350 | 351 | const Float32Array_: TypeValidator = ( 352 | target, 353 | ): target is Float32Array => { 354 | return target instanceof Float32Array || isTagged(target, "Float32Array"); 355 | }; 356 | setName(Float32Array_, "Float32Array"); 357 | export { Float32Array_ as Float32Array }; 358 | 359 | const Float64Array_: TypeValidator = ( 360 | target, 361 | ): target is Float64Array => { 362 | return target instanceof Float64Array || isTagged(target, "Float64Array"); 363 | }; 364 | setName(Float64Array_, "Float64Array"); 365 | export { Float64Array_ as Float64Array }; 366 | 367 | const anyTypeValidator: TypeValidator> = ( 368 | target, 369 | ): target is TypeValidator => { 370 | return typeof target === "function"; 371 | }; 372 | setName(anyTypeValidator, "anyTypeValidator"); 373 | export { anyTypeValidator }; 374 | 375 | const unknownTypeValidator: TypeValidator> = ( 376 | target, 377 | ): target is TypeValidator => { 378 | return typeof target === "function"; 379 | }; 380 | setName(unknownTypeValidator, "unknownTypeValidator"); 381 | export { unknownTypeValidator }; 382 | -------------------------------------------------------------------------------- /src/coerce.test.ts: -------------------------------------------------------------------------------- 1 | import { describe, test, expect } from "vitest"; 2 | import * as _t from "../dist/bundle.min"; 3 | import coerce, { 4 | $CoercingApiFunctions, 5 | assertType as coerce_assertType, 6 | } from "../coerce"; 7 | 8 | const t: typeof import("..") = _t as any; 9 | 10 | test("coercion", () => { 11 | const validators = [ 12 | null, 13 | undefined, 14 | true, 15 | false, 16 | "hello", 17 | 87, 18 | Symbol("hi!"), 19 | 69n, 20 | [], 21 | [1], 22 | [1, 2], 23 | ["hi", Boolean], 24 | { 25 | a: true, 26 | b: 43, 27 | [Symbol("heyo")]: Number, 28 | }, 29 | { 30 | very: { 31 | very: { 32 | very: { 33 | deeply: { 34 | nested: { 35 | something: String, 36 | }, 37 | }, 38 | }, 39 | }, 40 | }, 41 | }, 42 | String, 43 | Number, 44 | Boolean, 45 | BigInt, 46 | Symbol, 47 | RegExp, 48 | Array, 49 | Set, 50 | Map, 51 | Object, 52 | Date, 53 | Function, 54 | ArrayBuffer, 55 | SharedArrayBuffer, 56 | DataView, 57 | Int8Array, 58 | Uint8Array, 59 | Uint8ClampedArray, 60 | Int16Array, 61 | Uint16Array, 62 | Int32Array, 63 | Uint32Array, 64 | Float32Array, 65 | Float64Array, 66 | class MyClass {}, 67 | NaN, 68 | Infinity, 69 | -Infinity, 70 | ].map(coerce); 71 | 72 | for (const type of validators) { 73 | expect(typeof type).toBe("function"); 74 | } 75 | 76 | expect(validators.map((v) => v.name)).toMatchInlineSnapshot(` 77 | [ 78 | "null", 79 | "undefined", 80 | "true", 81 | "false", 82 | "exactString("hello")", 83 | "exactNumber(87)", 84 | "exactSymbol(Symbol(hi!))", 85 | "exactBigInt(69)", 86 | "arrayOfAny", 87 | "arrayOf(exactNumber(1))", 88 | "tuple(exactNumber(1), exactNumber(2))", 89 | "tuple(exactString("hi"), boolean)", 90 | "objectWithProperties({ a: true, b: exactNumber(43) })", 91 | "objectWithProperties({ very: objectWithProperties({ very: objectWithProperties({ very: objectWithProperties({ deeply: objectWithProperties({ nested: objectWithProperties({ something: string }) }) }) }) }) })", 92 | "string", 93 | "number", 94 | "boolean", 95 | "bigint", 96 | "Symbol", 97 | "RegExp", 98 | "anyArray", 99 | "anySet", 100 | "anyMap", 101 | "anyObject", 102 | "Date", 103 | "anyFunction", 104 | "ArrayBuffer", 105 | "SharedArrayBuffer", 106 | "DataView", 107 | "Int8Array", 108 | "Uint8Array", 109 | "Uint8ClampedArray", 110 | "Int16Array", 111 | "Uint16Array", 112 | "Int32Array", 113 | "Uint32Array", 114 | "Float32Array", 115 | "Float64Array", 116 | "instanceOf(MyClass)", 117 | "NaN", 118 | "Infinity", 119 | "NegativeInfinity", 120 | ] 121 | `); 122 | }); 123 | 124 | describe("$CoercingApiFunctions", () => { 125 | const { stringifyValue, isOfType, asType } = $CoercingApiFunctions; 126 | 127 | test("stringifyValue", () => { 128 | // same as normal api function 129 | expect(stringifyValue({ hi: "yeah" })).toBe( 130 | t.stringifyValue({ hi: "yeah" }), 131 | ); 132 | }); 133 | 134 | test("assertType", () => { 135 | // with type validator 136 | expect(() => { 137 | const thing: unknown = "blah"; 138 | coerce_assertType(thing, t.string); 139 | thing; // hover type here should be `string` 140 | }).not.toThrowError(); 141 | expect(() => { 142 | const thing: unknown = "blah"; 143 | coerce_assertType(thing, t.number); 144 | thing; // hover type here should be `number` 145 | }).toThrowErrorMatchingInlineSnapshot( 146 | `[TypeError: Expected value of type number, but received "blah"]`, 147 | ); 148 | 149 | // with coercion 150 | expect(() => { 151 | const thing: unknown = "blah"; 152 | coerce_assertType(thing, String); 153 | thing; // hover type here should be `string` 154 | }).not.toThrowError(); 155 | expect(() => { 156 | const thing: unknown = "blah"; 157 | coerce_assertType(thing, Number); 158 | thing; // hover type here should be `number` 159 | }).toThrowErrorMatchingInlineSnapshot( 160 | `[TypeError: Expected value of type number, but received "blah"]`, 161 | ); 162 | }); 163 | 164 | test("isOfType", () => { 165 | expect(isOfType("hi", t.string)).toBe(true); 166 | expect(isOfType("hi", String)).toBe(true); 167 | }); 168 | 169 | test("asType", () => { 170 | expect(asType("hi", t.number)).toBe("hi"); 171 | expect(asType("hi", Number)).toBe("hi"); 172 | }); 173 | }); 174 | -------------------------------------------------------------------------------- /src/coerce/api-functions.ts: -------------------------------------------------------------------------------- 1 | import * as t from ".."; 2 | import { TypeValidator } from "../type-validator"; 3 | import coerce, { type Unwrap, type Coerceable } from "./coerce"; 4 | 5 | export const stringifyValue = t.stringifyValue; 6 | 7 | const defaultMessageMaker = t.assertType.defaultMessageMaker; 8 | 9 | function _assertType | Coerceable>( 10 | target: any, 11 | type: T, 12 | messageMaker: ( 13 | target: any, 14 | expectedType: TypeValidator, 15 | ) => string = defaultMessageMaker, 16 | ErrorConstructor: { new (message?: string): any } = TypeError, 17 | ): asserts target is Unwrap { 18 | t.assertType(target, coerce(type), messageMaker, ErrorConstructor); 19 | } 20 | 21 | Object.defineProperties(_assertType, { 22 | name: { value: "assertType", configurable: true }, 23 | defaultMessageMaker: { 24 | get() { 25 | return defaultMessageMaker; 26 | }, 27 | }, 28 | }); 29 | 30 | export const assertType: { 31 | readonly defaultMessageMaker: typeof defaultMessageMaker; 32 | } & typeof _assertType = _assertType as any; 33 | 34 | export function isOfType | Coerceable>( 35 | target: any, 36 | type: T, 37 | ): target is Unwrap { 38 | return t.isOfType(target, coerce(type)); 39 | } 40 | 41 | const any: any = null; 42 | export function asType | Coerceable>( 43 | target: unknown, 44 | type: T = any, 45 | ): Unwrap { 46 | return target as any; 47 | } 48 | -------------------------------------------------------------------------------- /src/coerce/coerce.ts: -------------------------------------------------------------------------------- 1 | import * as t from ".."; 2 | import { TypeValidator } from "../type-validator"; 3 | 4 | // prettier-ignore 5 | export type CoerceValue = 6 | V extends StringConstructor ? TypeValidator 7 | : V extends NumberConstructor ? TypeValidator 8 | : V extends BooleanConstructor ? TypeValidator 9 | : V extends BigIntConstructor ? TypeValidator 10 | : V extends SymbolConstructor ? TypeValidator 11 | : V extends RegExpConstructor ? TypeValidator 12 | : V extends ArrayConstructor ? TypeValidator> 13 | : V extends SetConstructor ? TypeValidator> 14 | : V extends MapConstructor ? TypeValidator> 15 | : V extends ObjectConstructor ? TypeValidator<{ [key: string | number | symbol]: unknown }> 16 | : V extends DateConstructor ? TypeValidator 17 | : V extends FunctionConstructor ? TypeValidator 18 | : V extends ArrayBufferConstructor ? TypeValidator 19 | : V extends SharedArrayBufferConstructor ? TypeValidator 20 | : V extends DataViewConstructor ? TypeValidator 21 | : V extends Int8ArrayConstructor ? TypeValidator 22 | : V extends Uint8ArrayConstructor ? TypeValidator 23 | : V extends Uint8ClampedArrayConstructor ? TypeValidator 24 | : V extends Int16ArrayConstructor ? TypeValidator 25 | : V extends Uint16ArrayConstructor ? TypeValidator 26 | : V extends Int32ArrayConstructor ? TypeValidator 27 | : V extends Uint32ArrayConstructor ? TypeValidator 28 | : V extends Float32ArrayConstructor ? TypeValidator 29 | : V extends Float64ArrayConstructor ? TypeValidator 30 | : V extends RegExp ? TypeValidator 31 | : V extends {} ? TypeValidator<{ [key in keyof V]: CoerceValue }> 32 | : V extends [] ? TypeValidator<[]> 33 | : V extends [any] ? TypeValidator>> 34 | : V extends Array ? TypeValidator> 35 | : V extends { new (...args: any): any } ? TypeValidator> 36 | : TypeValidator; 37 | 38 | export type Coerceable = 39 | | boolean 40 | | number 41 | | string 42 | | bigint 43 | | undefined 44 | | null 45 | | RegExp 46 | | StringConstructor 47 | | NumberConstructor 48 | | BooleanConstructor 49 | | BigIntConstructor 50 | | SymbolConstructor 51 | | RegExpConstructor 52 | | ArrayConstructor 53 | | SetConstructor 54 | | MapConstructor 55 | | ObjectConstructor 56 | | DateConstructor 57 | | FunctionConstructor 58 | | ArrayBufferConstructor 59 | | SharedArrayBufferConstructor 60 | | DataViewConstructor 61 | | Int8ArrayConstructor 62 | | Uint8ArrayConstructor 63 | | Uint8ClampedArrayConstructor 64 | | Int16ArrayConstructor 65 | | Uint16ArrayConstructor 66 | | Int32ArrayConstructor 67 | | Uint32ArrayConstructor 68 | | Float32ArrayConstructor 69 | | Float64ArrayConstructor 70 | | {} 71 | | [] 72 | | [any] 73 | | Array 74 | | { new (...args: any): any }; 75 | 76 | // prettier-ignore 77 | export type Unwrap | unknown> = 78 | V extends TypeValidator ? T 79 | : V extends Coerceable ? 80 | CoerceValue extends TypeValidator ? T : never 81 | : unknown; 82 | 83 | const coerce: | unknown>( 84 | value: V, 85 | ) => TypeValidator> = (value: any): any => { 86 | if (t.null(value)) { 87 | return t.null; 88 | } else if (t.undefined(value)) { 89 | return t.undefined; 90 | } else if (t.true(value)) { 91 | return t.true; 92 | } else if (t.false(value)) { 93 | return t.false; 94 | } else if (t.NaN(value)) { 95 | return t.NaN; 96 | } else if (t.Infinity(value)) { 97 | return t.Infinity; 98 | } else if (t.NegativeInfinity(value)) { 99 | return t.NegativeInfinity; 100 | } else if (t.string(value)) { 101 | return t.exactString(value); 102 | } else if (t.number(value)) { 103 | return t.exactNumber(value); 104 | } else if (t.symbol(value)) { 105 | return t.exactSymbol(value); 106 | } else if (t.bigint(value)) { 107 | return t.exactBigInt(value); 108 | } else if (t.RegExp(value)) { 109 | return t.stringMatching(value); 110 | } else if (t.array(value)) { 111 | if (value.length < 1) { 112 | return t.arrayOfAny; 113 | } else if (value.length === 1) { 114 | return t.arrayOf(coerce(value[0])); 115 | } else { 116 | const validators = value.map(coerce); 117 | return t.tuple( 118 | // @ts-ignore spread in tuple position 119 | ...validators, 120 | ); 121 | } 122 | } else if (t.object(value)) { 123 | return t.objectWithProperties( 124 | Object.fromEntries( 125 | Object.entries(value).map(([key, value]) => { 126 | return [key, coerce(value)]; 127 | }), 128 | ), 129 | ); 130 | } else if (t.anyFunction(value)) { 131 | if (value === String) { 132 | return t.string; 133 | } else if (value === Number) { 134 | return t.number; 135 | } else if (value === Boolean) { 136 | return t.boolean; 137 | } else if (typeof BigInt !== "undefined" && value === BigInt) { 138 | return t.bigint; 139 | } else if (typeof Symbol !== "undefined" && value === Symbol) { 140 | return t.symbol; 141 | } else if (value === RegExp) { 142 | return t.RegExp; 143 | } else if (value === Array) { 144 | return t.anyArray; 145 | } else if ((value as any) === Set) { 146 | return t.anySet; 147 | } else if ((value as any) === Map) { 148 | return t.anyMap; 149 | } else if (value === Object) { 150 | return t.anyObject; 151 | } else if (value === Date) { 152 | return t.Date; 153 | } else if (value === Function) { 154 | return t.anyFunction; 155 | } else if ( 156 | typeof ArrayBuffer !== "undefined" && 157 | (value as any) === ArrayBuffer 158 | ) { 159 | return t.ArrayBuffer; 160 | } else if ( 161 | typeof SharedArrayBuffer !== "undefined" && 162 | (value as any) === SharedArrayBuffer 163 | ) { 164 | return t.SharedArrayBuffer; 165 | } else if (typeof DataView !== "undefined" && (value as any) === DataView) { 166 | return t.DataView; 167 | } else if ( 168 | typeof Int8Array !== "undefined" && 169 | (value as any) === Int8Array 170 | ) { 171 | return t.Int8Array; 172 | } else if ( 173 | typeof Uint8Array !== "undefined" && 174 | (value as any) === Uint8Array 175 | ) { 176 | return t.Uint8Array; 177 | } else if ( 178 | typeof Uint8ClampedArray !== "undefined" && 179 | (value as any) === Uint8ClampedArray 180 | ) { 181 | return t.Uint8ClampedArray; 182 | } else if ( 183 | typeof Int16Array !== "undefined" && 184 | (value as any) === Int16Array 185 | ) { 186 | return t.Int16Array; 187 | } else if ( 188 | typeof Uint16Array !== "undefined" && 189 | (value as any) === Uint16Array 190 | ) { 191 | return t.Uint16Array; 192 | } else if ( 193 | typeof Int32Array !== "undefined" && 194 | (value as any) === Int32Array 195 | ) { 196 | return t.Int32Array; 197 | } else if ( 198 | typeof Uint32Array !== "undefined" && 199 | (value as any) === Uint32Array 200 | ) { 201 | return t.Uint32Array; 202 | } else if ( 203 | typeof Float32Array !== "undefined" && 204 | (value as any) === Float32Array 205 | ) { 206 | return t.Float32Array; 207 | } else if ( 208 | typeof Float64Array !== "undefined" && 209 | (value as any) === Float64Array 210 | ) { 211 | return t.Float64Array; 212 | } else if (/class\s/.test(value.toString())) { 213 | return t.instanceOf(value); 214 | } else { 215 | // assume it's already a type validator 216 | return value as any; 217 | } 218 | } else { 219 | throw new Error( 220 | `Not clear how to coerce value to type: ${t.stringifyValue(value)}`, 221 | ); 222 | } 223 | }; 224 | 225 | export default coerce; 226 | 227 | const _coercingTypeConstructors: { 228 | [key: string]: (...args: any) => TypeValidator; 229 | } = { 230 | arrayOf: (type) => t.arrayOf(coerce(type)), 231 | intersection: (...args) => 232 | t.intersection( 233 | // @ts-ignore spread in tuple position 234 | ...args.map(coerce), 235 | ), 236 | and: (...args) => 237 | t.and( 238 | // @ts-ignore spread in tuple position 239 | ...args.map(coerce), 240 | ), 241 | union: (...args) => 242 | t.union( 243 | // @ts-ignore spread in tuple position 244 | ...args.map(coerce), 245 | ), 246 | or: (...args) => 247 | t.or( 248 | // @ts-ignore spread in tuple position 249 | ...args.map(coerce), 250 | ), 251 | mapOf: (k, v) => t.mapOf(coerce(k), coerce(v)), 252 | setOf: (type) => t.setOf(coerce(type)), 253 | maybe: (type) => t.maybe(coerce(type)), 254 | objectWithProperties: (properties) => 255 | t.objectWithProperties( 256 | Object.fromEntries( 257 | Object.entries(properties).map(([k, v]) => [k, coerce(v)]), 258 | ), 259 | ) as any, 260 | objectWithOnlyTheseProperties: (properties) => 261 | t.objectWithOnlyTheseProperties( 262 | Object.fromEntries( 263 | Object.entries(properties).map(([k, v]) => [k, coerce(v)]), 264 | ), 265 | ) as any, 266 | mappingObjectOf: (k, v) => t.mappingObjectOf(coerce(k), coerce(v)), 267 | record: (k, v) => t.record(coerce(k), coerce(v)), 268 | partialObjectWithProperties: (properties) => 269 | t.partialObjectWithProperties( 270 | Object.fromEntries( 271 | Object.entries(properties).map(([k, v]) => [k, coerce(v)]), 272 | ), 273 | ) as any, 274 | tuple: (...args) => 275 | t.tuple( 276 | // @ts-ignore spread in tuple position 277 | ...args.map(coerce), 278 | ) as any, 279 | }; 280 | 281 | namespace CoercingTypeConstructors { 282 | export declare function arrayOf< 283 | T extends TypeValidator | Coerceable | unknown, 284 | >(typeValidator: T): TypeValidator>>; 285 | 286 | interface IntersectionFn { 287 | < 288 | First extends TypeValidator | Coerceable | unknown, 289 | Second extends TypeValidator | Coerceable | unknown, 290 | >( 291 | first: First, 292 | second: Second, 293 | ): TypeValidator & Unwrap>; 294 | < 295 | First extends TypeValidator | Coerceable | unknown, 296 | Second extends TypeValidator | Coerceable | unknown, 297 | Third extends TypeValidator | Coerceable | unknown, 298 | >( 299 | first: First, 300 | second: Second, 301 | third: Third, 302 | ): TypeValidator & Unwrap & Unwrap>; 303 | < 304 | First extends TypeValidator | Coerceable | unknown, 305 | Second extends TypeValidator | Coerceable | unknown, 306 | Third extends TypeValidator | Coerceable | unknown, 307 | Fourth extends TypeValidator | Coerceable | unknown, 308 | >( 309 | first: First, 310 | second: Second, 311 | third: Third, 312 | fourth: Fourth, 313 | ): TypeValidator< 314 | Unwrap & Unwrap & Unwrap & Unwrap 315 | >; 316 | < 317 | First extends TypeValidator | Coerceable | unknown, 318 | Second extends TypeValidator | Coerceable | unknown, 319 | Third extends TypeValidator | Coerceable | unknown, 320 | Fourth extends TypeValidator | Coerceable | unknown, 321 | Fifth extends TypeValidator | Coerceable | unknown, 322 | >( 323 | first: First, 324 | second: Second, 325 | third: Third, 326 | fourth: Fourth, 327 | fifth: Fifth, 328 | ): TypeValidator< 329 | Unwrap & 330 | Unwrap & 331 | Unwrap & 332 | Unwrap & 333 | Unwrap 334 | >; 335 | < 336 | First extends TypeValidator | Coerceable | unknown, 337 | Second extends TypeValidator | Coerceable | unknown, 338 | Third extends TypeValidator | Coerceable | unknown, 339 | Fourth extends TypeValidator | Coerceable | unknown, 340 | Fifth extends TypeValidator | Coerceable | unknown, 341 | Sixth extends TypeValidator | Coerceable | unknown, 342 | >( 343 | first: First, 344 | second: Second, 345 | third: Third, 346 | fourth: Fourth, 347 | fifth: Fifth, 348 | sixth: Sixth, 349 | ): TypeValidator< 350 | Unwrap & 351 | Unwrap & 352 | Unwrap & 353 | Unwrap & 354 | Unwrap & 355 | Unwrap 356 | >; 357 | < 358 | First extends TypeValidator | Coerceable | unknown, 359 | Second extends TypeValidator | Coerceable | unknown, 360 | Third extends TypeValidator | Coerceable | unknown, 361 | Fourth extends TypeValidator | Coerceable | unknown, 362 | Fifth extends TypeValidator | Coerceable | unknown, 363 | Sixth extends TypeValidator | Coerceable | unknown, 364 | Seventh extends TypeValidator | Coerceable | unknown, 365 | >( 366 | first: First, 367 | second: Second, 368 | third: Third, 369 | fourth: Fourth, 370 | fifth: Fifth, 371 | sixth: Sixth, 372 | seventh: Seventh, 373 | ): TypeValidator< 374 | Unwrap & 375 | Unwrap & 376 | Unwrap & 377 | Unwrap & 378 | Unwrap & 379 | Unwrap & 380 | Unwrap 381 | >; 382 | < 383 | First extends TypeValidator | Coerceable | unknown, 384 | Second extends TypeValidator | Coerceable | unknown, 385 | Third extends TypeValidator | Coerceable | unknown, 386 | Fourth extends TypeValidator | Coerceable | unknown, 387 | Fifth extends TypeValidator | Coerceable | unknown, 388 | Sixth extends TypeValidator | Coerceable | unknown, 389 | Seventh extends TypeValidator | Coerceable | unknown, 390 | Eighth extends TypeValidator | Coerceable | unknown, 391 | >( 392 | first: First, 393 | second: Second, 394 | third: Third, 395 | fourth: Fourth, 396 | fifth: Fifth, 397 | sixth: Sixth, 398 | seventh: Seventh, 399 | eighth: Eighth, 400 | ): TypeValidator< 401 | Unwrap & 402 | Unwrap & 403 | Unwrap & 404 | Unwrap & 405 | Unwrap & 406 | Unwrap & 407 | Unwrap & 408 | Unwrap 409 | >; 410 | < 411 | First extends TypeValidator | Coerceable | unknown, 412 | Second extends TypeValidator | Coerceable | unknown, 413 | Third extends TypeValidator | Coerceable | unknown, 414 | Fourth extends TypeValidator | Coerceable | unknown, 415 | Fifth extends TypeValidator | Coerceable | unknown, 416 | Sixth extends TypeValidator | Coerceable | unknown, 417 | Seventh extends TypeValidator | Coerceable | unknown, 418 | Eighth extends TypeValidator | Coerceable | unknown, 419 | Ninth extends TypeValidator | Coerceable | unknown, 420 | >( 421 | first: First, 422 | second: Second, 423 | third: Third, 424 | fourth: Fourth, 425 | fifth: Fifth, 426 | sixth: Sixth, 427 | seventh: Seventh, 428 | eighth: Eighth, 429 | ninth: Ninth, 430 | ): TypeValidator< 431 | Unwrap & 432 | Unwrap & 433 | Unwrap & 434 | Unwrap & 435 | Unwrap & 436 | Unwrap & 437 | Unwrap & 438 | Unwrap & 439 | Unwrap 440 | >; 441 | < 442 | First extends TypeValidator | Coerceable | unknown, 443 | Second extends TypeValidator | Coerceable | unknown, 444 | Third extends TypeValidator | Coerceable | unknown, 445 | Fourth extends TypeValidator | Coerceable | unknown, 446 | Fifth extends TypeValidator | Coerceable | unknown, 447 | Sixth extends TypeValidator | Coerceable | unknown, 448 | Seventh extends TypeValidator | Coerceable | unknown, 449 | Eighth extends TypeValidator | Coerceable | unknown, 450 | Ninth extends TypeValidator | Coerceable | unknown, 451 | Tenth extends TypeValidator | Coerceable | unknown, 452 | >( 453 | first: First, 454 | second: Second, 455 | third: Third, 456 | fourth: Fourth, 457 | fifth: Fifth, 458 | sixth: Sixth, 459 | seventh: Seventh, 460 | eighth: Eighth, 461 | ninth: Ninth, 462 | tenth: Tenth, 463 | ): TypeValidator< 464 | Unwrap & 465 | Unwrap & 466 | Unwrap & 467 | Unwrap & 468 | Unwrap & 469 | Unwrap & 470 | Unwrap & 471 | Unwrap & 472 | Unwrap & 473 | Unwrap 474 | >; 475 | } 476 | export declare const intersection: IntersectionFn; 477 | export declare const and: IntersectionFn; 478 | 479 | export interface UnionFn { 480 | < 481 | First extends TypeValidator | Coerceable | unknown, 482 | Second extends TypeValidator | Coerceable | unknown, 483 | >( 484 | first: First, 485 | second: Second, 486 | ): TypeValidator | Unwrap>; 487 | < 488 | First extends TypeValidator | Coerceable | unknown, 489 | Second extends TypeValidator | Coerceable | unknown, 490 | Third extends TypeValidator | Coerceable | unknown, 491 | >( 492 | first: First, 493 | second: Second, 494 | third: Third, 495 | ): TypeValidator | Unwrap | Unwrap>; 496 | < 497 | First extends TypeValidator | Coerceable | unknown, 498 | Second extends TypeValidator | Coerceable | unknown, 499 | Third extends TypeValidator | Coerceable | unknown, 500 | Fourth extends TypeValidator | Coerceable | unknown, 501 | >( 502 | first: First, 503 | second: Second, 504 | third: Third, 505 | fourth: Fourth, 506 | ): TypeValidator< 507 | Unwrap | Unwrap | Unwrap | Unwrap 508 | >; 509 | < 510 | First extends TypeValidator | Coerceable | unknown, 511 | Second extends TypeValidator | Coerceable | unknown, 512 | Third extends TypeValidator | Coerceable | unknown, 513 | Fourth extends TypeValidator | Coerceable | unknown, 514 | Fifth extends TypeValidator | Coerceable | unknown, 515 | >( 516 | first: First, 517 | second: Second, 518 | third: Third, 519 | fourth: Fourth, 520 | fifth: Fifth, 521 | ): TypeValidator< 522 | | Unwrap 523 | | Unwrap 524 | | Unwrap 525 | | Unwrap 526 | | Unwrap 527 | >; 528 | < 529 | First extends TypeValidator | Coerceable | unknown, 530 | Second extends TypeValidator | Coerceable | unknown, 531 | Third extends TypeValidator | Coerceable | unknown, 532 | Fourth extends TypeValidator | Coerceable | unknown, 533 | Fifth extends TypeValidator | Coerceable | unknown, 534 | Sixth extends TypeValidator | Coerceable | unknown, 535 | >( 536 | first: First, 537 | second: Second, 538 | third: Third, 539 | fourth: Fourth, 540 | fifth: Fifth, 541 | sixth: Sixth, 542 | ): TypeValidator< 543 | | Unwrap 544 | | Unwrap 545 | | Unwrap 546 | | Unwrap 547 | | Unwrap 548 | | Unwrap 549 | >; 550 | < 551 | First extends TypeValidator | Coerceable | unknown, 552 | Second extends TypeValidator | Coerceable | unknown, 553 | Third extends TypeValidator | Coerceable | unknown, 554 | Fourth extends TypeValidator | Coerceable | unknown, 555 | Fifth extends TypeValidator | Coerceable | unknown, 556 | Sixth extends TypeValidator | Coerceable | unknown, 557 | Seventh extends TypeValidator | Coerceable | unknown, 558 | >( 559 | first: First, 560 | second: Second, 561 | third: Third, 562 | fourth: Fourth, 563 | fifth: Fifth, 564 | sixth: Sixth, 565 | seventh: Seventh, 566 | ): TypeValidator< 567 | | Unwrap 568 | | Unwrap 569 | | Unwrap 570 | | Unwrap 571 | | Unwrap 572 | | Unwrap 573 | | Unwrap 574 | >; 575 | < 576 | First extends TypeValidator | Coerceable | unknown, 577 | Second extends TypeValidator | Coerceable | unknown, 578 | Third extends TypeValidator | Coerceable | unknown, 579 | Fourth extends TypeValidator | Coerceable | unknown, 580 | Fifth extends TypeValidator | Coerceable | unknown, 581 | Sixth extends TypeValidator | Coerceable | unknown, 582 | Seventh extends TypeValidator | Coerceable | unknown, 583 | Eighth extends TypeValidator | Coerceable | unknown, 584 | >( 585 | first: First, 586 | second: Second, 587 | third: Third, 588 | fourth: Fourth, 589 | fifth: Fifth, 590 | sixth: Sixth, 591 | seventh: Seventh, 592 | eighth: Eighth, 593 | ): TypeValidator< 594 | | Unwrap 595 | | Unwrap 596 | | Unwrap 597 | | Unwrap 598 | | Unwrap 599 | | Unwrap 600 | | Unwrap 601 | | Unwrap 602 | >; 603 | < 604 | First extends TypeValidator | Coerceable | unknown, 605 | Second extends TypeValidator | Coerceable | unknown, 606 | Third extends TypeValidator | Coerceable | unknown, 607 | Fourth extends TypeValidator | Coerceable | unknown, 608 | Fifth extends TypeValidator | Coerceable | unknown, 609 | Sixth extends TypeValidator | Coerceable | unknown, 610 | Seventh extends TypeValidator | Coerceable | unknown, 611 | Eighth extends TypeValidator | Coerceable | unknown, 612 | Ninth extends TypeValidator | Coerceable | unknown, 613 | >( 614 | first: First, 615 | second: Second, 616 | third: Third, 617 | fourth: Fourth, 618 | fifth: Fifth, 619 | sixth: Sixth, 620 | seventh: Seventh, 621 | eighth: Eighth, 622 | ninth: Ninth, 623 | ): TypeValidator< 624 | | Unwrap 625 | | Unwrap 626 | | Unwrap 627 | | Unwrap 628 | | Unwrap 629 | | Unwrap 630 | | Unwrap 631 | | Unwrap 632 | | Unwrap 633 | >; 634 | < 635 | First extends TypeValidator | Coerceable | unknown, 636 | Second extends TypeValidator | Coerceable | unknown, 637 | Third extends TypeValidator | Coerceable | unknown, 638 | Fourth extends TypeValidator | Coerceable | unknown, 639 | Fifth extends TypeValidator | Coerceable | unknown, 640 | Sixth extends TypeValidator | Coerceable | unknown, 641 | Seventh extends TypeValidator | Coerceable | unknown, 642 | Eighth extends TypeValidator | Coerceable | unknown, 643 | Ninth extends TypeValidator | Coerceable | unknown, 644 | Tenth extends TypeValidator | Coerceable | unknown, 645 | >( 646 | first: First, 647 | second: Second, 648 | third: Third, 649 | fourth: Fourth, 650 | fifth: Fifth, 651 | sixth: Sixth, 652 | seventh: Seventh, 653 | eighth: Eighth, 654 | ninth: Ninth, 655 | tenth: Tenth, 656 | ): TypeValidator< 657 | | Unwrap 658 | | Unwrap 659 | | Unwrap 660 | | Unwrap 661 | | Unwrap 662 | | Unwrap 663 | | Unwrap 664 | | Unwrap 665 | | Unwrap 666 | | Unwrap 667 | >; 668 | } 669 | export declare const union: UnionFn; 670 | export declare const or: UnionFn; 671 | 672 | export declare function mapOf< 673 | K extends TypeValidator | Coerceable | unknown, 674 | V extends TypeValidator | Coerceable | unknown, 675 | >(keyType: K, valueType: V): TypeValidator, Unwrap>>; 676 | 677 | export declare function setOf< 678 | T extends TypeValidator | Coerceable | unknown, 679 | >(itemType: T): TypeValidator>>; 680 | 681 | export declare function maybe< 682 | T extends TypeValidator | Coerceable | unknown, 683 | >(itemType: T): TypeValidator | undefined | null>; 684 | 685 | export declare function objectWithProperties< 686 | T extends { 687 | [key: string | number | symbol]: 688 | | TypeValidator 689 | | Coerceable 690 | | unknown; 691 | }, 692 | >( 693 | properties: T, 694 | ): TypeValidator<{ 695 | [key in keyof T]: Unwrap; 696 | }>; 697 | 698 | export declare function objectWithOnlyTheseProperties< 699 | T extends { 700 | [key: string | number | symbol]: 701 | | TypeValidator 702 | | Coerceable 703 | | unknown; 704 | }, 705 | >( 706 | properties: T, 707 | ): TypeValidator<{ 708 | [key in keyof T]: Unwrap; 709 | }>; 710 | 711 | export declare function mappingObjectOf< 712 | Values extends TypeValidator | Coerceable | unknown, 713 | Keys extends TypeValidator | Coerceable | unknown, 714 | >( 715 | keyType: Keys, 716 | valueType: Values, 717 | ): TypeValidator< 718 | Record< 719 | Unwrap extends string | number | symbol ? Unwrap : never, 720 | Unwrap 721 | > 722 | >; 723 | export declare const record: typeof mappingObjectOf; 724 | 725 | export declare function partialObjectWithProperties< 726 | T extends { 727 | [key: string | number | symbol]: 728 | | TypeValidator 729 | | Coerceable 730 | | unknown; 731 | }, 732 | >( 733 | properties: T, 734 | ): TypeValidator<{ 735 | [key in keyof T]: Unwrap | null | undefined; 736 | }>; 737 | 738 | export interface TupleFn { 739 | < 740 | First extends TypeValidator | Coerceable | unknown, 741 | Second extends TypeValidator | Coerceable | unknown, 742 | >( 743 | first: First, 744 | second: Second, 745 | ): TypeValidator<[Unwrap, Unwrap]>; 746 | < 747 | First extends TypeValidator | Coerceable | unknown, 748 | Second extends TypeValidator | Coerceable | unknown, 749 | Third extends TypeValidator | Coerceable | unknown, 750 | >( 751 | first: First, 752 | second: Second, 753 | third: Third, 754 | ): TypeValidator<[Unwrap, Unwrap, Unwrap]>; 755 | < 756 | First extends TypeValidator | Coerceable | unknown, 757 | Second extends TypeValidator | Coerceable | unknown, 758 | Third extends TypeValidator | Coerceable | unknown, 759 | Fourth extends TypeValidator | Coerceable | unknown, 760 | >( 761 | first: First, 762 | second: Second, 763 | third: Third, 764 | fourth: Fourth, 765 | ): TypeValidator< 766 | [Unwrap, Unwrap, Unwrap, Unwrap] 767 | >; 768 | < 769 | First extends TypeValidator | Coerceable | unknown, 770 | Second extends TypeValidator | Coerceable | unknown, 771 | Third extends TypeValidator | Coerceable | unknown, 772 | Fourth extends TypeValidator | Coerceable | unknown, 773 | Fifth extends TypeValidator | Coerceable | unknown, 774 | >( 775 | first: First, 776 | second: Second, 777 | third: Third, 778 | fourth: Fourth, 779 | fifth: Fifth, 780 | ): TypeValidator< 781 | [ 782 | Unwrap, 783 | Unwrap, 784 | Unwrap, 785 | Unwrap, 786 | Unwrap, 787 | ] 788 | >; 789 | < 790 | First extends TypeValidator | Coerceable | unknown, 791 | Second extends TypeValidator | Coerceable | unknown, 792 | Third extends TypeValidator | Coerceable | unknown, 793 | Fourth extends TypeValidator | Coerceable | unknown, 794 | Fifth extends TypeValidator | Coerceable | unknown, 795 | Sixth extends TypeValidator | Coerceable | unknown, 796 | >( 797 | first: First, 798 | second: Second, 799 | third: Third, 800 | fourth: Fourth, 801 | fifth: Fifth, 802 | sixth: Sixth, 803 | ): TypeValidator< 804 | [ 805 | Unwrap, 806 | Unwrap, 807 | Unwrap, 808 | Unwrap, 809 | Unwrap, 810 | Unwrap, 811 | ] 812 | >; 813 | < 814 | First extends TypeValidator | Coerceable | unknown, 815 | Second extends TypeValidator | Coerceable | unknown, 816 | Third extends TypeValidator | Coerceable | unknown, 817 | Fourth extends TypeValidator | Coerceable | unknown, 818 | Fifth extends TypeValidator | Coerceable | unknown, 819 | Sixth extends TypeValidator | Coerceable | unknown, 820 | Seventh extends TypeValidator | Coerceable | unknown, 821 | >( 822 | first: First, 823 | second: Second, 824 | third: Third, 825 | fourth: Fourth, 826 | fifth: Fifth, 827 | sixth: Sixth, 828 | seventh: Seventh, 829 | ): TypeValidator< 830 | [ 831 | Unwrap, 832 | Unwrap, 833 | Unwrap, 834 | Unwrap, 835 | Unwrap, 836 | Unwrap, 837 | Unwrap, 838 | ] 839 | >; 840 | < 841 | First extends TypeValidator | Coerceable | unknown, 842 | Second extends TypeValidator | Coerceable | unknown, 843 | Third extends TypeValidator | Coerceable | unknown, 844 | Fourth extends TypeValidator | Coerceable | unknown, 845 | Fifth extends TypeValidator | Coerceable | unknown, 846 | Sixth extends TypeValidator | Coerceable | unknown, 847 | Seventh extends TypeValidator | Coerceable | unknown, 848 | Eighth extends TypeValidator | Coerceable | unknown, 849 | >( 850 | first: First, 851 | second: Second, 852 | third: Third, 853 | fourth: Fourth, 854 | fifth: Fifth, 855 | sixth: Sixth, 856 | seventh: Seventh, 857 | eighth: Eighth, 858 | ): TypeValidator< 859 | [ 860 | Unwrap, 861 | Unwrap, 862 | Unwrap, 863 | Unwrap, 864 | Unwrap, 865 | Unwrap, 866 | Unwrap, 867 | Unwrap, 868 | ] 869 | >; 870 | < 871 | First extends TypeValidator | Coerceable | unknown, 872 | Second extends TypeValidator | Coerceable | unknown, 873 | Third extends TypeValidator | Coerceable | unknown, 874 | Fourth extends TypeValidator | Coerceable | unknown, 875 | Fifth extends TypeValidator | Coerceable | unknown, 876 | Sixth extends TypeValidator | Coerceable | unknown, 877 | Seventh extends TypeValidator | Coerceable | unknown, 878 | Eighth extends TypeValidator | Coerceable | unknown, 879 | Ninth extends TypeValidator | Coerceable | unknown, 880 | >( 881 | first: First, 882 | second: Second, 883 | third: Third, 884 | fourth: Fourth, 885 | fifth: Fifth, 886 | sixth: Sixth, 887 | seventh: Seventh, 888 | eighth: Eighth, 889 | ninth: Ninth, 890 | ): TypeValidator< 891 | [ 892 | Unwrap, 893 | Unwrap, 894 | Unwrap, 895 | Unwrap, 896 | Unwrap, 897 | Unwrap, 898 | Unwrap, 899 | Unwrap, 900 | Unwrap, 901 | ] 902 | >; 903 | < 904 | First extends TypeValidator | Coerceable | unknown, 905 | Second extends TypeValidator | Coerceable | unknown, 906 | Third extends TypeValidator | Coerceable | unknown, 907 | Fourth extends TypeValidator | Coerceable | unknown, 908 | Fifth extends TypeValidator | Coerceable | unknown, 909 | Sixth extends TypeValidator | Coerceable | unknown, 910 | Seventh extends TypeValidator | Coerceable | unknown, 911 | Eighth extends TypeValidator | Coerceable | unknown, 912 | Ninth extends TypeValidator | Coerceable | unknown, 913 | Tenth extends TypeValidator | Coerceable | unknown, 914 | >( 915 | first: First, 916 | second: Second, 917 | third: Third, 918 | fourth: Fourth, 919 | fifth: Fifth, 920 | sixth: Sixth, 921 | seventh: Seventh, 922 | eighth: Eighth, 923 | ninth: Ninth, 924 | tenth: Tenth, 925 | ): TypeValidator< 926 | [ 927 | Unwrap, 928 | Unwrap, 929 | Unwrap, 930 | Unwrap, 931 | Unwrap, 932 | Unwrap, 933 | Unwrap, 934 | Unwrap, 935 | Unwrap, 936 | Unwrap, 937 | ] 938 | >; 939 | } 940 | export declare const tuple: TupleFn; 941 | } 942 | 943 | export const $CoercingTypeConstructors: typeof CoercingTypeConstructors = 944 | _coercingTypeConstructors as any; 945 | -------------------------------------------------------------------------------- /src/coerce/index.ts: -------------------------------------------------------------------------------- 1 | export { 2 | $CoercingTypeConstructors, 3 | type CoerceValue, 4 | type Coerceable, 5 | type Unwrap, 6 | } from "./coerce"; 7 | 8 | // The raw "coerce" function 9 | import coerce from "./coerce"; 10 | export default coerce; 11 | export { coerce }; 12 | 13 | export * from "./api-functions"; 14 | import * as $CoercingApiFunctions from "./api-functions"; 15 | export { $CoercingApiFunctions }; 16 | 17 | // Basic type constructors behave the same whether using coerce or not, but are 18 | // exported here for parity with the named exports of the normal index.ts 19 | export * from "../basic-types"; 20 | 21 | // Again, no difference in these types when using coerce, but provided for named 22 | // export parity 23 | export type { 24 | TypeValidator, 25 | ExtractTypeFromValidator, 26 | } from "../type-validator"; 27 | -------------------------------------------------------------------------------- /src/index.test.ts: -------------------------------------------------------------------------------- 1 | import { test, expect } from "vitest"; 2 | import * as t from "../dist/bundle.min"; 3 | 4 | test("basic test", () => { 5 | expect(t.isOfType("hi", t.string)).toBe(true); 6 | expect(() => { 7 | t.assertType("hi", t.string); 8 | }).not.toThrowError(); 9 | 10 | const someObject = {}; 11 | expect(t.asType(someObject)).toBe(someObject); 12 | expect(t.asType(someObject, t.string)).toBe(someObject); 13 | 14 | expect(t.isOfType(43, t.string)).toBe(false); 15 | expect(() => { 16 | t.assertType(43, t.string); 17 | }).toThrowErrorMatchingInlineSnapshot( 18 | `[TypeError: Expected value of type string, but received 43]`, 19 | ); 20 | 21 | expect( 22 | t.isOfType( 23 | 43, 24 | t.objectWithProperties({ 25 | potato: t.true, 26 | }), 27 | ), 28 | ).toBe(false); 29 | 30 | expect(() => { 31 | t.assertType( 32 | 43, 33 | t.objectWithProperties({ 34 | potato: t.true, 35 | }), 36 | ); 37 | }).toThrowErrorMatchingInlineSnapshot( 38 | `[TypeError: Expected value of type objectWithProperties({ potato: true }), but received 43]`, 39 | ); 40 | }); 41 | 42 | test("assertType value formatting", () => { 43 | expect(() => { 44 | t.assertType({ potato: false }, t.string); 45 | }).toThrowErrorMatchingInlineSnapshot( 46 | `[TypeError: Expected value of type string, but received {"potato":false}]`, 47 | ); 48 | 49 | expect(() => { 50 | t.assertType( 51 | { 52 | potato: function greenThumb() {}, 53 | eggplant: () => {}, 54 | }, 55 | t.string, 56 | ); 57 | }).toThrowErrorMatchingInlineSnapshot( 58 | `[TypeError: Expected value of type string, but received {"potato":"","eggplant":""}]`, 59 | ); 60 | 61 | class Something { 62 | num = 42; 63 | someMethod() {} 64 | } 65 | const something = new Something(); 66 | 67 | expect(() => { 68 | t.assertType(something, t.string); 69 | }).toThrowErrorMatchingInlineSnapshot( 70 | `[TypeError: Expected value of type string, but received ""]`, 71 | ); 72 | 73 | expect(() => { 74 | t.assertType( 75 | new Map([ 76 | [1, 2], 77 | [3, 4], 78 | ]), 79 | t.string, 80 | ); 81 | }).toThrowErrorMatchingInlineSnapshot( 82 | `[TypeError: Expected value of type string, but received {"":[[1,2],[3,4]]}]`, 83 | ); 84 | 85 | expect(() => { 86 | t.assertType(new Set([1, 2, 3, 4, 5, 6]), t.string); 87 | }).toThrowErrorMatchingInlineSnapshot( 88 | `[TypeError: Expected value of type string, but received {"":[1,2,3,4,5,6]}]`, 89 | ); 90 | 91 | expect(() => { 92 | t.assertType(undefined, t.string); 93 | }).toThrowErrorMatchingInlineSnapshot( 94 | `[TypeError: Expected value of type string, but received ""]`, 95 | ); 96 | 97 | expect(() => { 98 | t.assertType(BigInt("34895084309843905843905840935890438509"), t.string); 99 | }).toThrowErrorMatchingInlineSnapshot( 100 | `[TypeError: Expected value of type string, but received ""]`, 101 | ); 102 | 103 | expect(() => { 104 | t.assertType(Symbol("hi"), t.string); 105 | }).toThrowErrorMatchingInlineSnapshot( 106 | `[TypeError: Expected value of type string, but received ""]`, 107 | ); 108 | 109 | expect(() => { 110 | t.assertType(Symbol(), t.string); 111 | }).toThrowErrorMatchingInlineSnapshot( 112 | `[TypeError: Expected value of type string, but received ""]`, 113 | ); 114 | 115 | expect(() => { 116 | t.assertType(Array.isArray, t.string); 117 | }).toThrowErrorMatchingInlineSnapshot( 118 | `[TypeError: Expected value of type string, but received ""]`, 119 | ); 120 | 121 | expect(() => { 122 | t.assertType(Symbol, t.string); 123 | }).toThrowErrorMatchingInlineSnapshot( 124 | `[TypeError: Expected value of type string, but received ""]`, 125 | ); 126 | 127 | expect(() => { 128 | t.assertType(() => {}, t.string); 129 | }).toThrowErrorMatchingInlineSnapshot( 130 | `[TypeError: Expected value of type string, but received ""]`, 131 | ); 132 | 133 | expect(() => { 134 | const obj = {}; 135 | // @ts-ignore 136 | obj.obj = obj; 137 | 138 | t.assertType(obj, t.string); 139 | }).toThrowErrorMatchingInlineSnapshot( 140 | `[TypeError: Expected value of type string, but received {"[object Object] that failed to serialize due to error":"TypeError: Converting circular structure to JSON\\n --> starting at object with constructor 'Object'\\n --- property 'obj' closes the circle"}]`, 141 | ); 142 | }); 143 | 144 | test("basic types", () => { 145 | expect(t.isOfType(324325432, t.any)).toBe(true); 146 | expect(t.isOfType({}, t.any)).toBe(true); 147 | expect(t.isOfType(null, t.any)).toBe(true); 148 | 149 | expect(t.isOfType(324325432, t.unknown)).toBe(true); 150 | expect(t.isOfType({}, t.unknown)).toBe(true); 151 | expect(t.isOfType(null, t.unknown)).toBe(true); 152 | 153 | expect(t.isOfType(354267, t.anyObject)).toBe(false); 154 | expect(t.isOfType(null, t.anyObject)).toBe(false); 155 | expect(t.isOfType({ one: 1 }, t.anyObject)).toBe(true); 156 | 157 | expect(t.isOfType(354267, t.unknownObject)).toBe(false); 158 | expect(t.isOfType(null, t.unknownObject)).toBe(false); 159 | expect(t.isOfType({ one: 1 }, t.unknownObject)).toBe(true); 160 | 161 | expect(t.isOfType(354267, t.object)).toBe(false); 162 | expect(t.isOfType(null, t.object)).toBe(false); 163 | expect(t.isOfType({ one: 1 }, t.object)).toBe(true); 164 | 165 | expect(t.isOfType(354267, t.Object)).toBe(false); 166 | expect(t.isOfType(null, t.Object)).toBe(false); 167 | expect(t.isOfType({ one: 1 }, t.Object)).toBe(true); 168 | 169 | expect(t.isOfType(354267, t.objectOrNull)).toBe(false); 170 | expect(t.isOfType(null, t.objectOrNull)).toBe(true); 171 | expect(t.isOfType({ one: 1 }, t.objectOrNull)).toBe(true); 172 | 173 | expect(t.isOfType(354267, t.arrayOfAny)).toBe(false); 174 | expect(t.isOfType([1, "f", null], t.arrayOfAny)).toBe(true); 175 | 176 | expect(t.isOfType(354267, t.arrayOfUnknown)).toBe(false); 177 | expect(t.isOfType([1, "f", null], t.arrayOfUnknown)).toBe(true); 178 | 179 | expect(t.isOfType(354267, t.array)).toBe(false); 180 | expect(t.isOfType([1, "f", null], t.array)).toBe(true); 181 | 182 | expect(t.isOfType(354267, t.Array)).toBe(false); 183 | expect(t.isOfType([1, "f", null], t.Array)).toBe(true); 184 | 185 | expect(t.isOfType(354267, t.anyArray)).toBe(false); 186 | expect(t.isOfType([1, "f", null], t.anyArray)).toBe(true); 187 | 188 | expect(t.isOfType(354267, t.boolean)).toBe(false); 189 | expect(t.isOfType(null, t.boolean)).toBe(false); 190 | expect(t.isOfType(true, t.boolean)).toBe(true); 191 | expect(t.isOfType(false, t.boolean)).toBe(true); 192 | 193 | expect(t.isOfType(354267, t.Boolean)).toBe(false); 194 | expect(t.isOfType(null, t.Boolean)).toBe(false); 195 | expect(t.isOfType(true, t.Boolean)).toBe(true); 196 | expect(t.isOfType(false, t.Boolean)).toBe(true); 197 | 198 | expect(t.isOfType(354267, t.string)).toBe(false); 199 | expect(t.isOfType("hi", t.string)).toBe(true); 200 | 201 | expect(t.isOfType(354267, t.String)).toBe(false); 202 | expect(t.isOfType("hi", t.String)).toBe(true); 203 | 204 | expect(t.isOfType(354267, t.null)).toBe(false); 205 | expect(t.isOfType(undefined, t.null)).toBe(false); 206 | expect(t.isOfType(null, t.null)).toBe(true); 207 | expect(t.null.name).toBe("null"); 208 | 209 | expect(t.isOfType(354267, t.undefined)).toBe(false); 210 | expect(t.isOfType(null, t.undefined)).toBe(false); 211 | expect(t.isOfType(undefined, t.undefined)).toBe(true); 212 | expect(t.undefined.name).toBe("undefined"); 213 | 214 | expect(t.isOfType(354267, t.nullish)).toBe(false); 215 | expect(t.isOfType(false, t.nullish)).toBe(false); 216 | expect(t.isOfType(null, t.nullish)).toBe(true); 217 | expect(t.isOfType(undefined, t.nullish)).toBe(true); 218 | 219 | expect(t.isOfType(354267, t.void)).toBe(false); 220 | expect(t.isOfType(false, t.void)).toBe(false); 221 | expect(t.isOfType(null, t.void)).toBe(true); 222 | expect(t.isOfType(undefined, t.void)).toBe(true); 223 | 224 | expect(t.isOfType("hi", t.numberIncludingNanAndInfinities)).toBe(false); 225 | expect(t.isOfType(43875, t.numberIncludingNanAndInfinities)).toBe(true); 226 | expect(t.isOfType(NaN, t.numberIncludingNanAndInfinities)).toBe(true); 227 | expect(t.isOfType(Infinity, t.numberIncludingNanAndInfinities)).toBe(true); 228 | expect(t.isOfType(-Infinity, t.numberIncludingNanAndInfinities)).toBe(true); 229 | 230 | expect(t.isOfType("hi", t.number)).toBe(false); 231 | expect(t.isOfType(43875, t.number)).toBe(true); 232 | expect(t.isOfType(NaN, t.number)).toBe(false); 233 | expect(t.isOfType(Infinity, t.number)).toBe(false); 234 | expect(t.isOfType(-Infinity, t.number)).toBe(false); 235 | 236 | expect(t.isOfType("hi", t.Number)).toBe(false); 237 | expect(t.isOfType(43875, t.Number)).toBe(true); 238 | expect(t.isOfType(NaN, t.Number)).toBe(false); 239 | expect(t.isOfType(Infinity, t.Number)).toBe(false); 240 | expect(t.isOfType(-Infinity, t.Number)).toBe(false); 241 | 242 | expect(t.isOfType("hi", t.NaN)).toBe(false); 243 | expect(t.isOfType(327489, t.NaN)).toBe(false); 244 | expect(t.isOfType(NaN, t.NaN)).toBe(true); 245 | // @ts-ignore object is possibly undefined 246 | expect(t.isOfType(undefined - 5, t.NaN)).toBe(true); 247 | expect(t.NaN.name).toBe("NaN"); 248 | 249 | expect(t.isOfType("hi", t.Infinity)).toBe(false); 250 | expect(t.isOfType(327489, t.Infinity)).toBe(false); 251 | expect(t.isOfType(NaN, t.Infinity)).toBe(false); 252 | expect(t.isOfType(Infinity, t.Infinity)).toBe(true); 253 | expect(t.isOfType(-Infinity, t.Infinity)).toBe(false); 254 | expect(t.Infinity.name).toBe("Infinity"); 255 | 256 | expect(t.isOfType("hi", t.NegativeInfinity)).toBe(false); 257 | expect(t.isOfType(327489, t.NegativeInfinity)).toBe(false); 258 | expect(t.isOfType(NaN, t.NegativeInfinity)).toBe(false); 259 | expect(t.isOfType(Infinity, t.NegativeInfinity)).toBe(false); 260 | expect(t.isOfType(-Infinity, t.NegativeInfinity)).toBe(true); 261 | 262 | expect(t.isOfType("hi", t.integer)).toBe(false); 263 | expect(t.isOfType(6, t.integer)).toBe(true); 264 | expect(t.isOfType(6.5, t.integer)).toBe(false); 265 | 266 | expect(t.isOfType("hi", t.bigint)).toBe(false); 267 | expect(t.isOfType(6, t.bigint)).toBe(false); 268 | expect(t.isOfType(6n, t.bigint)).toBe(true); 269 | 270 | expect(t.isOfType("hi", t.BigInt)).toBe(false); 271 | expect(t.isOfType(6, t.BigInt)).toBe(false); 272 | expect(t.isOfType(6n, t.BigInt)).toBe(true); 273 | 274 | expect(t.isOfType("hi", t.never)).toBe(false); 275 | expect(t.isOfType({}, t.never)).toBe(false); 276 | expect(t.isOfType(4376859, t.never)).toBe(false); 277 | 278 | expect(t.isOfType("hi", t.anyFunction)).toBe(false); 279 | expect(t.isOfType(() => {}, t.anyFunction)).toBe(true); 280 | expect(t.isOfType(function blah() {}, t.anyFunction)).toBe(true); 281 | 282 | expect(t.isOfType("hi", t.unknownFunction)).toBe(false); 283 | expect(t.isOfType(() => {}, t.unknownFunction)).toBe(true); 284 | expect(t.isOfType(function blah() {}, t.unknownFunction)).toBe(true); 285 | 286 | expect(t.isOfType("hi", t.Function)).toBe(false); 287 | expect(t.isOfType(() => {}, t.Function)).toBe(true); 288 | expect(t.isOfType(function blah() {}, t.Function)).toBe(true); 289 | 290 | expect(t.isOfType("hi", t.false)).toBe(false); 291 | expect(t.isOfType(0, t.false)).toBe(false); 292 | expect(t.isOfType("", t.false)).toBe(false); 293 | expect(t.isOfType(true, t.false)).toBe(false); 294 | expect(t.isOfType(false, t.false)).toBe(true); 295 | expect(t.false.name).toBe("false"); 296 | 297 | expect(t.isOfType("hi", t.true)).toBe(false); 298 | expect(t.isOfType(1, t.true)).toBe(false); 299 | expect(t.isOfType(false, t.true)).toBe(false); 300 | expect(t.isOfType(true, t.true)).toBe(true); 301 | expect(t.true.name).toBe("true"); 302 | 303 | expect(t.isOfType("hi", t.falsy)).toBe(false); 304 | expect(t.isOfType(1, t.falsy)).toBe(false); 305 | expect(t.isOfType(true, t.falsy)).toBe(false); 306 | expect(t.isOfType(0, t.falsy)).toBe(true); 307 | expect(t.isOfType("", t.falsy)).toBe(true); 308 | expect(t.isOfType(null, t.falsy)).toBe(true); 309 | expect(t.isOfType(undefined, t.falsy)).toBe(true); 310 | expect(t.isOfType(false, t.falsy)).toBe(true); 311 | 312 | expect(t.isOfType("hi", t.truthy)).toBe(true); 313 | expect(t.isOfType(1, t.truthy)).toBe(true); 314 | expect(t.isOfType(true, t.truthy)).toBe(true); 315 | expect(t.isOfType(0, t.truthy)).toBe(false); 316 | expect(t.isOfType("", t.truthy)).toBe(false); 317 | expect(t.isOfType(null, t.truthy)).toBe(false); 318 | expect(t.isOfType(undefined, t.truthy)).toBe(false); 319 | expect(t.isOfType(false, t.truthy)).toBe(false); 320 | 321 | expect(t.isOfType("hi", t.nonNullOrUndefined)).toBe(true); 322 | expect(t.isOfType(3246, t.nonNullOrUndefined)).toBe(true); 323 | expect(t.isOfType(false, t.nonNullOrUndefined)).toBe(true); 324 | expect(t.isOfType(null, t.nonNullOrUndefined)).toBe(false); 325 | expect(t.isOfType(undefined, t.nonNullOrUndefined)).toBe(false); 326 | 327 | expect(t.isOfType("hi", t.Error)).toBe(false); 328 | expect(t.isOfType(new Error("uh oh"), t.Error)).toBe(true); 329 | expect(t.Error.name).toBe("Error"); 330 | 331 | expect(t.isOfType("hi", t.Symbol)).toBe(false); 332 | expect(t.isOfType(Symbol("hi"), t.Symbol)).toBe(true); 333 | expect(t.isOfType(Symbol(), t.Symbol)).toBe(true); 334 | expect(t.Symbol.name).toBe("Symbol"); 335 | 336 | expect(t.isOfType("hi", t.symbol)).toBe(false); 337 | expect(t.isOfType(Symbol("hi"), t.symbol)).toBe(true); 338 | expect(t.isOfType(Symbol(), t.symbol)).toBe(true); 339 | 340 | expect(t.isOfType(null, t.RegExp)).toBe(false); 341 | expect(t.isOfType("hi", t.RegExp)).toBe(false); 342 | expect(t.isOfType(/abc$/, t.RegExp)).toBe(true); 343 | expect(t.isOfType(new RegExp("hi"), t.RegExp)).toBe(true); 344 | expect(t.isOfType(/^\w[0-9]+$/g, t.RegExp)).toBe(true); 345 | 346 | expect(t.isOfType(null, t.Date)).toBe(false); 347 | expect(t.isOfType(Date.now(), t.Date)).toBe(false); 348 | expect(t.isOfType(3456743959834, t.Date)).toBe(false); 349 | expect(t.isOfType(new Date(), t.Date)).toBe(true); 350 | expect(t.isOfType(new Date(326324782), t.Date)).toBe(true); 351 | 352 | expect(t.isOfType("hi", t.anyMap)).toBe(false); 353 | expect(t.isOfType(new Map(), t.anyMap)).toBe(true); 354 | expect( 355 | t.isOfType( 356 | new Map([ 357 | [1, 2], 358 | ["three", "four"], 359 | ]), 360 | t.anyMap, 361 | ), 362 | ).toBe(true); 363 | 364 | expect(t.isOfType("hi", t.unknownMap)).toBe(false); 365 | expect(t.isOfType(new Map(), t.unknownMap)).toBe(true); 366 | expect( 367 | t.isOfType( 368 | new Map([ 369 | [1, 2], 370 | ["three", "four"], 371 | ]), 372 | t.unknownMap, 373 | ), 374 | ).toBe(true); 375 | 376 | expect(t.isOfType("hi", t.map)).toBe(false); 377 | expect(t.isOfType(new Map(), t.map)).toBe(true); 378 | expect( 379 | t.isOfType( 380 | new Map([ 381 | [1, 2], 382 | ["three", "four"], 383 | ]), 384 | t.map, 385 | ), 386 | ).toBe(true); 387 | 388 | expect(t.isOfType("hi", t.Map)).toBe(false); 389 | expect(t.isOfType(new Map(), t.Map)).toBe(true); 390 | expect( 391 | t.isOfType( 392 | new Map([ 393 | [1, 2], 394 | ["three", "four"], 395 | ]), 396 | t.Map, 397 | ), 398 | ).toBe(true); 399 | 400 | expect(t.isOfType("hi", t.anySet)).toBe(false); 401 | expect(t.isOfType(new Set(), t.anySet)).toBe(true); 402 | expect(t.isOfType(new Set([1, 2, "three"]), t.anySet)).toBe(true); 403 | 404 | expect(t.isOfType("hi", t.unknownSet)).toBe(false); 405 | expect(t.isOfType(new Set(), t.unknownSet)).toBe(true); 406 | expect(t.isOfType(new Set([1, 2, "three"]), t.unknownSet)).toBe(true); 407 | 408 | expect(t.isOfType("hi", t.set)).toBe(false); 409 | expect(t.isOfType(new Set(), t.set)).toBe(true); 410 | expect(t.isOfType(new Set([1, 2, "three"]), t.set)).toBe(true); 411 | 412 | expect(t.isOfType("hi", t.Set)).toBe(false); 413 | expect(t.isOfType(new Set(), t.Set)).toBe(true); 414 | expect(t.isOfType(new Set([1, 2, "three"]), t.Set)).toBe(true); 415 | }); 416 | 417 | test("basic types - typed arrays", () => { 418 | expect(t.isOfType(null, t.ArrayBuffer)).toBe(false); 419 | expect(t.isOfType([], t.ArrayBuffer)).toBe(false); 420 | expect(t.isOfType(new ArrayBuffer(1024), t.ArrayBuffer)).toBe(true); 421 | 422 | expect(t.isOfType(null, t.SharedArrayBuffer)).toBe(false); 423 | expect(t.isOfType([], t.SharedArrayBuffer)).toBe(false); 424 | expect(t.isOfType(new ArrayBuffer(1024), t.SharedArrayBuffer)).toBe(false); 425 | expect(t.isOfType(new SharedArrayBuffer(1024), t.SharedArrayBuffer)).toBe( 426 | true, 427 | ); 428 | 429 | expect(t.isOfType(null, t.DataView)).toBe(false); 430 | expect(t.isOfType([], t.DataView)).toBe(false); 431 | expect(t.isOfType(new DataView(new ArrayBuffer(1024)), t.DataView)).toBe( 432 | true, 433 | ); 434 | 435 | expect(t.isOfType(null, t.TypedArray)).toBe(false); 436 | expect(t.isOfType([], t.TypedArray)).toBe(false); 437 | expect(t.isOfType(new Int8Array(4), t.TypedArray)).toBe(true); 438 | expect(t.isOfType(new Uint8Array(4), t.TypedArray)).toBe(true); 439 | expect(t.isOfType(new Uint8ClampedArray(4), t.TypedArray)).toBe(true); 440 | expect(t.isOfType(new Int16Array(4), t.TypedArray)).toBe(true); 441 | expect(t.isOfType(new Uint16Array(4), t.TypedArray)).toBe(true); 442 | expect(t.isOfType(new Int32Array(4), t.TypedArray)).toBe(true); 443 | expect(t.isOfType(new Uint32Array(4), t.TypedArray)).toBe(true); 444 | expect(t.isOfType(new Float32Array(4), t.TypedArray)).toBe(true); 445 | expect(t.isOfType(new Float64Array(8), t.TypedArray)).toBe(true); 446 | 447 | expect(t.isOfType(null, t.Int8Array)).toBe(false); 448 | expect(t.isOfType([], t.Int8Array)).toBe(false); 449 | expect(t.isOfType(new Int8Array(4), t.Int8Array)).toBe(true); 450 | expect(t.isOfType(new Uint8Array(4), t.Int8Array)).toBe(false); 451 | expect(t.isOfType(new Uint8ClampedArray(4), t.Int8Array)).toBe(false); 452 | expect(t.isOfType(new Int16Array(4), t.Int8Array)).toBe(false); 453 | expect(t.isOfType(new Uint16Array(4), t.Int8Array)).toBe(false); 454 | expect(t.isOfType(new Int32Array(4), t.Int8Array)).toBe(false); 455 | expect(t.isOfType(new Uint32Array(4), t.Int8Array)).toBe(false); 456 | expect(t.isOfType(new Float32Array(4), t.Int8Array)).toBe(false); 457 | expect(t.isOfType(new Float64Array(8), t.Int8Array)).toBe(false); 458 | 459 | expect(t.isOfType(null, t.Uint8Array)).toBe(false); 460 | expect(t.isOfType([], t.Uint8Array)).toBe(false); 461 | expect(t.isOfType(new Int8Array(4), t.Uint8Array)).toBe(false); 462 | expect(t.isOfType(new Uint8Array(4), t.Uint8Array)).toBe(true); 463 | expect(t.isOfType(new Uint8ClampedArray(4), t.Uint8Array)).toBe(false); 464 | expect(t.isOfType(new Int16Array(4), t.Uint8Array)).toBe(false); 465 | expect(t.isOfType(new Uint16Array(4), t.Uint8Array)).toBe(false); 466 | expect(t.isOfType(new Int32Array(4), t.Uint8Array)).toBe(false); 467 | expect(t.isOfType(new Uint32Array(4), t.Uint8Array)).toBe(false); 468 | expect(t.isOfType(new Float32Array(4), t.Uint8Array)).toBe(false); 469 | expect(t.isOfType(new Float64Array(8), t.Uint8Array)).toBe(false); 470 | 471 | expect(t.isOfType(null, t.Uint8ClampedArray)).toBe(false); 472 | expect(t.isOfType([], t.Uint8ClampedArray)).toBe(false); 473 | expect(t.isOfType(new Int8Array(4), t.Uint8ClampedArray)).toBe(false); 474 | expect(t.isOfType(new Uint8Array(4), t.Uint8ClampedArray)).toBe(false); 475 | expect(t.isOfType(new Uint8ClampedArray(4), t.Uint8ClampedArray)).toBe(true); 476 | expect(t.isOfType(new Int16Array(4), t.Uint8ClampedArray)).toBe(false); 477 | expect(t.isOfType(new Uint16Array(4), t.Uint8ClampedArray)).toBe(false); 478 | expect(t.isOfType(new Int32Array(4), t.Uint8ClampedArray)).toBe(false); 479 | expect(t.isOfType(new Uint32Array(4), t.Uint8ClampedArray)).toBe(false); 480 | expect(t.isOfType(new Float32Array(4), t.Uint8ClampedArray)).toBe(false); 481 | expect(t.isOfType(new Float64Array(8), t.Uint8ClampedArray)).toBe(false); 482 | 483 | expect(t.isOfType(null, t.Int16Array)).toBe(false); 484 | expect(t.isOfType([], t.Int16Array)).toBe(false); 485 | expect(t.isOfType(new Int8Array(4), t.Int16Array)).toBe(false); 486 | expect(t.isOfType(new Uint8Array(4), t.Int16Array)).toBe(false); 487 | expect(t.isOfType(new Uint8ClampedArray(4), t.Int16Array)).toBe(false); 488 | expect(t.isOfType(new Int16Array(4), t.Int16Array)).toBe(true); 489 | expect(t.isOfType(new Uint16Array(4), t.Int16Array)).toBe(false); 490 | expect(t.isOfType(new Int32Array(4), t.Int16Array)).toBe(false); 491 | expect(t.isOfType(new Uint32Array(4), t.Int16Array)).toBe(false); 492 | expect(t.isOfType(new Float32Array(4), t.Int16Array)).toBe(false); 493 | expect(t.isOfType(new Float64Array(8), t.Int16Array)).toBe(false); 494 | 495 | expect(t.isOfType(null, t.Uint16Array)).toBe(false); 496 | expect(t.isOfType([], t.Uint16Array)).toBe(false); 497 | expect(t.isOfType(new Int8Array(4), t.Uint16Array)).toBe(false); 498 | expect(t.isOfType(new Uint8Array(4), t.Uint16Array)).toBe(false); 499 | expect(t.isOfType(new Uint8ClampedArray(4), t.Uint16Array)).toBe(false); 500 | expect(t.isOfType(new Int16Array(4), t.Uint16Array)).toBe(false); 501 | expect(t.isOfType(new Uint16Array(4), t.Uint16Array)).toBe(true); 502 | expect(t.isOfType(new Int32Array(4), t.Uint16Array)).toBe(false); 503 | expect(t.isOfType(new Uint32Array(4), t.Uint16Array)).toBe(false); 504 | expect(t.isOfType(new Float32Array(4), t.Uint16Array)).toBe(false); 505 | expect(t.isOfType(new Float64Array(8), t.Uint16Array)).toBe(false); 506 | 507 | expect(t.isOfType(null, t.Int32Array)).toBe(false); 508 | expect(t.isOfType([], t.Int32Array)).toBe(false); 509 | expect(t.isOfType(new Int8Array(4), t.Int32Array)).toBe(false); 510 | expect(t.isOfType(new Uint8Array(4), t.Int32Array)).toBe(false); 511 | expect(t.isOfType(new Uint8ClampedArray(4), t.Int32Array)).toBe(false); 512 | expect(t.isOfType(new Int16Array(4), t.Int32Array)).toBe(false); 513 | expect(t.isOfType(new Uint16Array(4), t.Int32Array)).toBe(false); 514 | expect(t.isOfType(new Int32Array(4), t.Int32Array)).toBe(true); 515 | expect(t.isOfType(new Uint32Array(4), t.Int32Array)).toBe(false); 516 | expect(t.isOfType(new Float32Array(4), t.Int32Array)).toBe(false); 517 | expect(t.isOfType(new Float64Array(8), t.Int32Array)).toBe(false); 518 | 519 | expect(t.isOfType(null, t.Uint32Array)).toBe(false); 520 | expect(t.isOfType([], t.Uint32Array)).toBe(false); 521 | expect(t.isOfType(new Int8Array(4), t.Uint32Array)).toBe(false); 522 | expect(t.isOfType(new Uint8Array(4), t.Uint32Array)).toBe(false); 523 | expect(t.isOfType(new Uint8ClampedArray(4), t.Uint32Array)).toBe(false); 524 | expect(t.isOfType(new Int16Array(4), t.Uint32Array)).toBe(false); 525 | expect(t.isOfType(new Uint16Array(4), t.Uint32Array)).toBe(false); 526 | expect(t.isOfType(new Int32Array(4), t.Uint32Array)).toBe(false); 527 | expect(t.isOfType(new Uint32Array(4), t.Uint32Array)).toBe(true); 528 | expect(t.isOfType(new Float32Array(4), t.Uint32Array)).toBe(false); 529 | expect(t.isOfType(new Float64Array(8), t.Uint32Array)).toBe(false); 530 | 531 | expect(t.isOfType(null, t.Float32Array)).toBe(false); 532 | expect(t.isOfType([], t.Float32Array)).toBe(false); 533 | expect(t.isOfType(new Int8Array(4), t.Float32Array)).toBe(false); 534 | expect(t.isOfType(new Uint8Array(4), t.Float32Array)).toBe(false); 535 | expect(t.isOfType(new Uint8ClampedArray(4), t.Float32Array)).toBe(false); 536 | expect(t.isOfType(new Int16Array(4), t.Float32Array)).toBe(false); 537 | expect(t.isOfType(new Uint16Array(4), t.Float32Array)).toBe(false); 538 | expect(t.isOfType(new Int32Array(4), t.Float32Array)).toBe(false); 539 | expect(t.isOfType(new Uint32Array(4), t.Float32Array)).toBe(false); 540 | expect(t.isOfType(new Float32Array(4), t.Float32Array)).toBe(true); 541 | expect(t.isOfType(new Float64Array(8), t.Float32Array)).toBe(false); 542 | 543 | expect(t.isOfType(null, t.Float64Array)).toBe(false); 544 | expect(t.isOfType([], t.Float64Array)).toBe(false); 545 | expect(t.isOfType(new Int8Array(4), t.Float64Array)).toBe(false); 546 | expect(t.isOfType(new Uint8Array(4), t.Float64Array)).toBe(false); 547 | expect(t.isOfType(new Uint8ClampedArray(4), t.Float64Array)).toBe(false); 548 | expect(t.isOfType(new Int16Array(4), t.Float64Array)).toBe(false); 549 | expect(t.isOfType(new Uint16Array(4), t.Float64Array)).toBe(false); 550 | expect(t.isOfType(new Int32Array(4), t.Float64Array)).toBe(false); 551 | expect(t.isOfType(new Uint32Array(4), t.Float64Array)).toBe(false); 552 | expect(t.isOfType(new Float32Array(4), t.Float64Array)).toBe(false); 553 | expect(t.isOfType(new Float64Array(8), t.Float64Array)).toBe(true); 554 | }); 555 | 556 | test("type constructors", () => { 557 | { 558 | const type = t.arrayOf(t.number); 559 | expect(t.isOfType(324325432, type)).toBe(false); 560 | expect(t.isOfType(["hi"], type)).toBe(false); 561 | expect(t.isOfType([], type)).toBe(true); 562 | expect(t.isOfType([1, 2, 3], type)).toBe(true); 563 | expect(type.name).toMatchInlineSnapshot('"arrayOf(number)"'); 564 | } 565 | 566 | { 567 | const type = t.exactString("hello"); 568 | expect(t.isOfType(324325432, type)).toBe(false); 569 | expect(t.isOfType("hi", type)).toBe(false); 570 | expect(t.isOfType("hello", type)).toBe(true); 571 | expect(type.name).toMatchInlineSnapshot(`"exactString("hello")"`); 572 | } 573 | 574 | { 575 | const type = t.exactNumber(42); 576 | expect(t.isOfType("hi", type)).toBe(false); 577 | expect(t.isOfType(324325432, type)).toBe(false); 578 | expect(t.isOfType(42, type)).toBe(true); 579 | expect(type.name).toMatchInlineSnapshot('"exactNumber(42)"'); 580 | } 581 | 582 | { 583 | const sym = Symbol("potatoes"); 584 | const sym2 = Symbol("potatoes"); 585 | const type = t.exactSymbol(sym); 586 | expect(t.isOfType("hi", type)).toBe(false); 587 | expect(t.isOfType(324325432, type)).toBe(false); 588 | expect(t.isOfType(sym, type)).toBe(true); 589 | expect(t.isOfType(sym2, type)).toBe(false); 590 | expect(type.name).toMatchInlineSnapshot('"exactSymbol(Symbol(potatoes))"'); 591 | } 592 | 593 | { 594 | const type = t.exactBigInt(42n); 595 | expect(t.isOfType("hi", type)).toBe(false); 596 | expect(t.isOfType(324325432, type)).toBe(false); 597 | expect(t.isOfType(42, type)).toBe(false); 598 | expect(t.isOfType(42n, type)).toBe(true); 599 | expect(type.name).toMatchInlineSnapshot('"exactBigInt(42)"'); 600 | } 601 | 602 | { 603 | const type = t.hasClassName("Number"); 604 | expect(t.isOfType("hi", type)).toBe(false); 605 | expect(t.isOfType(42, type)).toBe(true); 606 | expect(type.name).toMatchInlineSnapshot(`"hasClassName("Number")"`); 607 | } 608 | 609 | { 610 | const type = t.hasClassName("Boolean"); 611 | expect(t.isOfType("hi", type)).toBe(false); 612 | expect(t.isOfType(true, type)).toBe(true); 613 | expect(type.name).toMatchInlineSnapshot(`"hasClassName("Boolean")"`); 614 | } 615 | 616 | { 617 | const type = t.hasToStringTag("Null"); 618 | expect(t.isOfType(null, type)).toBe(true); 619 | expect(t.isOfType({}, type)).toBe(false); 620 | expect(type.name).toMatchInlineSnapshot(`"hasToStringTag("Null")"`); 621 | } 622 | 623 | { 624 | class Something {} 625 | const something = new Something(); 626 | 627 | const type = t.instanceOf(Something); 628 | expect(t.isOfType("hi", type)).toBe(false); 629 | expect(t.isOfType(something, type)).toBe(true); 630 | expect(type.name).toMatchInlineSnapshot('"instanceOf(Something)"'); 631 | } 632 | 633 | { 634 | const type = t.intersection( 635 | t.objectWithProperties({ one: t.number }), 636 | t.objectWithProperties({ two: t.number }), 637 | ); 638 | expect(t.isOfType("hi", type)).toBe(false); 639 | expect(t.isOfType({}, type)).toBe(false); 640 | expect(t.isOfType({ one: 1 }, type)).toBe(false); 641 | expect(t.isOfType({ two: 2 }, type)).toBe(false); 642 | expect(t.isOfType({ one: 1, two: 2 }, type)).toBe(true); 643 | expect(type.name).toMatchInlineSnapshot( 644 | '"intersection(objectWithProperties({ one: number }), objectWithProperties({ two: number }))"', 645 | ); 646 | } 647 | 648 | { 649 | const type = t.and( 650 | t.objectWithProperties({ one: t.number }), 651 | t.objectWithProperties({ two: t.number }), 652 | ); 653 | expect(t.isOfType("hi", type)).toBe(false); 654 | expect(t.isOfType({}, type)).toBe(false); 655 | expect(t.isOfType({ one: 1 }, type)).toBe(false); 656 | expect(t.isOfType({ two: 2 }, type)).toBe(false); 657 | expect(t.isOfType({ one: 1, two: 2 }, type)).toBe(true); 658 | expect(type.name).toMatchInlineSnapshot( 659 | '"intersection(objectWithProperties({ one: number }), objectWithProperties({ two: number }))"', 660 | ); 661 | } 662 | 663 | { 664 | const type = t.union(t.number, t.string); 665 | expect(t.isOfType(null, type)).toBe(false); 666 | expect(t.isOfType({}, type)).toBe(false); 667 | expect(t.isOfType(45, type)).toBe(true); 668 | expect(t.isOfType("bees", type)).toBe(true); 669 | expect(type.name).toMatchInlineSnapshot('"union(number, string)"'); 670 | } 671 | 672 | { 673 | const type = t.or(t.number, t.string); 674 | expect(t.isOfType(null, type)).toBe(false); 675 | expect(t.isOfType({}, type)).toBe(false); 676 | expect(t.isOfType(45, type)).toBe(true); 677 | expect(t.isOfType("bees", type)).toBe(true); 678 | expect(type.name).toMatchInlineSnapshot('"union(number, string)"'); 679 | } 680 | 681 | { 682 | const type = t.mapOf(t.number, t.string); 683 | expect(t.isOfType({}, type)).toBe(false); 684 | expect(t.isOfType(new Map(), type)).toBe(true); 685 | expect( 686 | t.isOfType( 687 | new Map([ 688 | [1, "one"], 689 | [2, "two"], 690 | ]), 691 | type, 692 | ), 693 | ).toBe(true); 694 | expect( 695 | t.isOfType( 696 | new Map([ 697 | ["one", 1], 698 | ["two", 2], 699 | ]), 700 | type, 701 | ), 702 | ).toBe(false); 703 | expect(type.name).toMatchInlineSnapshot('"mapOf(number, string)"'); 704 | } 705 | 706 | { 707 | const type = t.setOf(t.number); 708 | expect(t.isOfType({}, type)).toBe(false); 709 | expect(t.isOfType(new Set(), type)).toBe(true); 710 | expect(t.isOfType(new Set([1, 2, 3]), type)).toBe(true); 711 | expect(t.isOfType(new Set(["blah", "blaaah"]), type)).toBe(false); 712 | expect(t.isOfType(new Set([1, 2, false]), type)).toBe(false); 713 | expect(type.name).toMatchInlineSnapshot('"setOf(number)"'); 714 | } 715 | 716 | { 717 | const type = t.maybe(t.number); 718 | expect(t.isOfType({}, type)).toBe(false); 719 | expect(t.isOfType(false, type)).toBe(false); 720 | expect(t.isOfType(0, type)).toBe(true); 721 | expect(t.isOfType(5, type)).toBe(true); 722 | expect(t.isOfType(null, type)).toBe(true); 723 | expect(t.isOfType(undefined, type)).toBe(true); 724 | expect(type.name).toMatchInlineSnapshot('"maybe(number)"'); 725 | } 726 | 727 | { 728 | const type = t.optional(t.number); 729 | expect(t.isOfType({}, type)).toBe(false); 730 | expect(t.isOfType(false, type)).toBe(false); 731 | expect(t.isOfType(0, type)).toBe(true); 732 | expect(t.isOfType(5, type)).toBe(true); 733 | expect(t.isOfType(null, type)).toBe(false); 734 | expect(t.isOfType(undefined, type)).toBe(true); 735 | expect(type.name).toMatchInlineSnapshot('"optional(number)"'); 736 | } 737 | 738 | { 739 | const type = t.objectWithProperties({ one: t.number, two: t.string }); 740 | expect(t.isOfType({}, type)).toBe(false); 741 | expect(t.isOfType({ one: 1 }, type)).toBe(false); 742 | expect(t.isOfType({ two: "hi" }, type)).toBe(false); 743 | expect(t.isOfType({ one: 1, two: "hi" }, type)).toBe(true); 744 | expect(t.isOfType({ one: 1, two: "hi", three: "yes" }, type)).toBe(true); 745 | 746 | expect(type.name).toMatchInlineSnapshot( 747 | '"objectWithProperties({ one: number, two: string })"', 748 | ); 749 | } 750 | 751 | { 752 | const type = t.objectWithOnlyTheseProperties({ 753 | one: t.number, 754 | two: t.string, 755 | }); 756 | expect(t.isOfType({}, type)).toBe(false); 757 | expect(t.isOfType({ one: 1 }, type)).toBe(false); 758 | expect(t.isOfType({ two: "hi" }, type)).toBe(false); 759 | expect(t.isOfType({ one: 1, two: "hi" }, type)).toBe(true); 760 | expect(t.isOfType({ one: 1, two: "hi", three: "yes" }, type)).toBe(false); 761 | 762 | expect(type.name).toMatchInlineSnapshot( 763 | '"objectWithOnlyTheseProperties({ one: number, two: string })"', 764 | ); 765 | } 766 | 767 | { 768 | const type = t.mappingObjectOf(t.string, t.number); 769 | expect(t.isOfType({}, type)).toBe(true); 770 | expect(t.isOfType({ hi: "hi" }, type)).toBe(false); 771 | expect(t.isOfType({ hi: 4 }, type)).toBe(true); 772 | expect(t.isOfType({ hi: 4, there: "hi" }, type)).toBe(false); 773 | expect(t.isOfType({ hi: 4, there: 5 }, type)).toBe(true); 774 | 775 | expect(type.name).toMatchInlineSnapshot( 776 | '"mappingObjectOf(string, number)"', 777 | ); 778 | } 779 | 780 | { 781 | const type = t.record(t.string, t.number); 782 | expect(t.isOfType({}, type)).toBe(true); 783 | expect(t.isOfType({ hi: "hi" }, type)).toBe(false); 784 | expect(t.isOfType({ hi: 4 }, type)).toBe(true); 785 | expect(t.isOfType({ hi: 4, there: "hi" }, type)).toBe(false); 786 | expect(t.isOfType({ hi: 4, there: 5 }, type)).toBe(true); 787 | 788 | expect(type.name).toMatchInlineSnapshot( 789 | '"mappingObjectOf(string, number)"', 790 | ); 791 | } 792 | 793 | { 794 | const type = t.partialObjectWithProperties({ 795 | one: t.string, 796 | two: t.boolean, 797 | }); 798 | expect(t.isOfType(45, type)).toBe(false); 799 | expect(t.isOfType({}, type)).toBe(true); 800 | expect(t.isOfType({ one: "hi" }, type)).toBe(true); 801 | expect(t.isOfType({ one: 45 }, type)).toBe(false); 802 | expect(t.isOfType({ two: true }, type)).toBe(true); 803 | expect(t.isOfType({ two: 66 }, type)).toBe(false); 804 | expect(t.isOfType({ one: "hi", two: true }, type)).toBe(true); 805 | expect(t.isOfType({ one: "hi", two: 42 }, type)).toBe(false); 806 | expect(t.isOfType({ one: null, two: undefined }, type)).toBe(true); 807 | 808 | expect(type.name).toMatchInlineSnapshot( 809 | '"partialObjectWithProperties({ one: union(string, null, undefined), two: union(boolean, null, undefined) })"', 810 | ); 811 | } 812 | 813 | { 814 | const type = t.stringMatching(/hi/g); 815 | expect(t.isOfType(45, type)).toBe(false); 816 | expect(t.isOfType("", type)).toBe(false); 817 | expect(t.isOfType("hey", type)).toBe(false); 818 | expect(t.isOfType("hi", type)).toBe(true); 819 | expect(t.isOfType("hiiiii", type)).toBe(true); // important: can match more than once 820 | expect(t.isOfType("uhhhhhiiiii", type)).toBe(true); 821 | 822 | expect(type.name).toMatchInlineSnapshot('"stringMatching(/hi/g)"'); 823 | } 824 | 825 | { 826 | const type = t.symbolFor("blah"); 827 | expect(t.isOfType(45, type)).toBe(false); 828 | expect(t.isOfType(Symbol(), type)).toBe(false); 829 | expect(t.isOfType(Symbol("blah"), type)).toBe(false); 830 | expect(t.isOfType(Symbol.for("blah"), type)).toBe(true); 831 | 832 | expect(type.name).toMatchInlineSnapshot(`"symbolFor("blah")"`); 833 | } 834 | 835 | { 836 | const type = t.tuple(t.number, t.string); 837 | expect(t.isOfType(45, type)).toBe(false); 838 | expect(t.isOfType([45], type)).toBe(false); 839 | expect(t.isOfType([45, 46], type)).toBe(false); 840 | expect(t.isOfType([45, "hi"], type)).toBe(true); 841 | expect(t.isOfType(["hi", 33], type)).toBe(false); 842 | 843 | expect(type.name).toMatchInlineSnapshot('"tuple(number, string)"'); 844 | } 845 | }); 846 | 847 | test("exported categories", () => { 848 | expect(t.$ApiFunctions).not.toBeUndefined(); 849 | 850 | for (const key in t.$ApiFunctions) { 851 | expect(t[key]).not.toBeUndefined(); 852 | } 853 | 854 | expect(t.$BasicTypes).not.toBeUndefined(); 855 | 856 | for (const key in t.$BasicTypes) { 857 | expect(t[key]).not.toBeUndefined(); 858 | } 859 | 860 | expect(t.$TypeConstructors).not.toBeUndefined(); 861 | 862 | for (const key in t.$TypeConstructors) { 863 | expect(t[key]).not.toBeUndefined(); 864 | } 865 | }); 866 | 867 | test("default message maker", () => { 868 | const message = t.assertType.defaultMessageMaker( 869 | { something: new Set([1, new Map([[{}, { five: 5 }]]), 3]) }, 870 | t.number, 871 | ); 872 | expect(message).toMatchInlineSnapshot( 873 | `"Expected value of type number, but received {"something":{"":[1,{"":[[{},{"five":5}]]},3]}}"`, 874 | ); 875 | }); 876 | 877 | test("default message maker with incorrect type validator", () => { 878 | const message = t.assertType.defaultMessageMaker( 879 | { something: new Set([1, new Map([[{}, { five: 5 }]]), 3]) }, 880 | { potato: null }, 881 | ); 882 | expect(message).toMatchInlineSnapshot( 883 | `"Expected value of type , but received {"something":{"":[1,{"":[[{},{"five":5}]]},3]}}"`, 884 | ); 885 | }); 886 | 887 | test("custom error constructor", () => { 888 | class MyError extends Error {} 889 | 890 | try { 891 | t.assertType({}, t.string, t.assertType.defaultMessageMaker, MyError); 892 | } catch (err) { 893 | expect(err).toBeInstanceOf(MyError); 894 | expect(err.message).toMatchInlineSnapshot( 895 | '"Expected value of type string, but received {}"', 896 | ); 897 | } 898 | }); 899 | 900 | test("type constructors throw errors when passed invalid args", () => { 901 | expect(() => { 902 | t.arrayOf(5); 903 | }).toThrowErrorMatchingInlineSnapshot( 904 | `[TypeError: Expected value of type anyTypeValidator, but received 5]`, 905 | ); 906 | 907 | expect(() => { 908 | t.exactString(5); 909 | }).toThrowErrorMatchingInlineSnapshot( 910 | `[TypeError: Expected value of type string, but received 5]`, 911 | ); 912 | 913 | expect(() => { 914 | t.exactNumber({}); 915 | }).toThrowErrorMatchingInlineSnapshot( 916 | `[TypeError: Expected value of type numberIncludingNanAndInfinities, but received {}]`, 917 | ); 918 | 919 | expect(() => { 920 | t.exactBigInt(5); 921 | }).toThrowErrorMatchingInlineSnapshot( 922 | `[TypeError: Expected value of type bigint, but received 5]`, 923 | ); 924 | 925 | expect(() => { 926 | t.exactSymbol(5); 927 | }).toThrowErrorMatchingInlineSnapshot( 928 | `[TypeError: Expected value of type Symbol, but received 5]`, 929 | ); 930 | 931 | expect(() => { 932 | t.hasClassName(5); 933 | }).toThrowErrorMatchingInlineSnapshot( 934 | `[TypeError: Expected value of type string, but received 5]`, 935 | ); 936 | 937 | expect(() => { 938 | t.hasToStringTag(5); 939 | }).toThrowErrorMatchingInlineSnapshot( 940 | `[TypeError: Expected value of type string, but received 5]`, 941 | ); 942 | 943 | expect(() => { 944 | t.intersection(5, 6); 945 | }).toThrowErrorMatchingInlineSnapshot( 946 | `[TypeError: Expected value of type arrayOf(anyTypeValidator), but received [5,6]]`, 947 | ); 948 | 949 | expect(() => { 950 | t.and(5, 6); 951 | }).toThrowErrorMatchingInlineSnapshot( 952 | `[TypeError: Expected value of type arrayOf(anyTypeValidator), but received [5,6]]`, 953 | ); 954 | 955 | expect(() => { 956 | t.union(5, 6); 957 | }).toThrowErrorMatchingInlineSnapshot( 958 | `[TypeError: Expected value of type arrayOf(anyTypeValidator), but received [5,6]]`, 959 | ); 960 | 961 | expect(() => { 962 | t.or(5, 6); 963 | }).toThrowErrorMatchingInlineSnapshot( 964 | `[TypeError: Expected value of type arrayOf(anyTypeValidator), but received [5,6]]`, 965 | ); 966 | 967 | expect(() => { 968 | t.instanceOf(5); 969 | }).toThrowErrorMatchingInlineSnapshot( 970 | `[TypeError: Expected value of type union(anyFunction, objectWithProperties({ [Symbol(Symbol.hasInstance)]: anyFunction })), but received 5]`, 971 | ); 972 | 973 | expect(() => { 974 | t.instanceOf({}); 975 | }).toThrowErrorMatchingInlineSnapshot( 976 | `[TypeError: Expected value of type union(anyFunction, objectWithProperties({ [Symbol(Symbol.hasInstance)]: anyFunction })), but received {}]`, 977 | ); 978 | 979 | expect(() => { 980 | const objectThatCanHaveInstance = { 981 | [Symbol.hasInstance]: () => false, 982 | }; 983 | 984 | t.instanceOf(objectThatCanHaveInstance); 985 | }).not.toThrowError(); 986 | 987 | expect(() => { 988 | t.mapOf(5, 6); 989 | }).toThrowErrorMatchingInlineSnapshot( 990 | `[TypeError: Expected value of type anyTypeValidator, but received 5]`, 991 | ); 992 | 993 | expect(() => { 994 | t.setOf(5); 995 | }).toThrowErrorMatchingInlineSnapshot( 996 | `[TypeError: Expected value of type anyTypeValidator, but received 5]`, 997 | ); 998 | 999 | expect(() => { 1000 | t.maybe(5); 1001 | }).toThrowErrorMatchingInlineSnapshot( 1002 | `[TypeError: Expected value of type anyTypeValidator, but received 5]`, 1003 | ); 1004 | 1005 | expect(() => { 1006 | t.objectWithProperties(5); 1007 | }).toThrowErrorMatchingInlineSnapshot( 1008 | `[TypeError: Expected value of type anyObject, but received 5]`, 1009 | ); 1010 | 1011 | expect(() => { 1012 | t.objectWithProperties(null); 1013 | }).toThrowErrorMatchingInlineSnapshot( 1014 | `[TypeError: Expected value of type anyObject, but received null]`, 1015 | ); 1016 | 1017 | expect(() => { 1018 | t.objectWithOnlyTheseProperties(null); 1019 | }).toThrowErrorMatchingInlineSnapshot( 1020 | `[TypeError: Expected value of type anyObject, but received null]`, 1021 | ); 1022 | 1023 | expect(() => { 1024 | t.mappingObjectOf(null, 5); 1025 | }).toThrowErrorMatchingInlineSnapshot( 1026 | `[TypeError: Expected value of type anyTypeValidator, but received null]`, 1027 | ); 1028 | 1029 | expect(() => { 1030 | t.partialObjectWithProperties(17); 1031 | }).toThrowErrorMatchingInlineSnapshot( 1032 | `[TypeError: Expected value of type anyObject, but received 17]`, 1033 | ); 1034 | 1035 | expect(() => { 1036 | t.stringMatching(435); 1037 | }).toThrowErrorMatchingInlineSnapshot( 1038 | `[TypeError: Expected value of type RegExp, but received 435]`, 1039 | ); 1040 | 1041 | expect(() => { 1042 | t.symbolFor(435); 1043 | }).toThrowErrorMatchingInlineSnapshot( 1044 | `[TypeError: Expected value of type string, but received 435]`, 1045 | ); 1046 | 1047 | expect(() => { 1048 | t.tuple(1, 2, {}); 1049 | }).toThrowErrorMatchingInlineSnapshot( 1050 | `[TypeError: Expected value of type arrayOf(anyTypeValidator), but received [1,2,{}]]`, 1051 | ); 1052 | }); 1053 | 1054 | test("api functions throw errors when passed invalid type validators", () => { 1055 | expect(() => { 1056 | t.assertType(null, null); 1057 | }).toThrowErrorMatchingInlineSnapshot( 1058 | `[TypeError: 'type' argument passed into 'assertType' was the wrong type. It should be a function, but it was: null]`, 1059 | ); 1060 | 1061 | expect(() => { 1062 | t.isOfType(null, null); 1063 | }).toThrowErrorMatchingInlineSnapshot( 1064 | `[TypeError: 'type' argument passed into 'isOfType' was the wrong type. It should be a function, but it was: null]`, 1065 | ); 1066 | }); 1067 | -------------------------------------------------------------------------------- /src/index.ts: -------------------------------------------------------------------------------- 1 | export * from "./api-functions"; 2 | import * as $ApiFunctions from "./api-functions"; 3 | export { $ApiFunctions }; 4 | 5 | export * from "./basic-types"; 6 | import * as $BasicTypes from "./basic-types"; 7 | export { $BasicTypes }; 8 | 9 | export * from "./type-constructors"; 10 | import * as $TypeConstructors from "./type-constructors"; 11 | export { $TypeConstructors }; 12 | 13 | export type { TypeValidator, ExtractTypeFromValidator } from "./type-validator"; 14 | -------------------------------------------------------------------------------- /src/type-constructors.ts: -------------------------------------------------------------------------------- 1 | import type { TypeValidator } from "./type-validator"; 2 | import * as basicTypes from "./basic-types"; 3 | import { assertType } from "./api-functions"; 4 | import { setName, hasOwn, allEntries } from "./utils"; 5 | 6 | export function objectStr(obj: { [key: string | number | symbol]: string }) { 7 | return `{ ${allEntries(obj) 8 | .map( 9 | ([key, value]) => 10 | `${typeof key === "string" ? key : `[${String(key)}]`}: ${value}`, 11 | ) 12 | .join(", ")} }`; 13 | } 14 | 15 | export function arrayOf( 16 | typeValidator: TypeValidator, 17 | ): TypeValidator> { 18 | assertType(typeValidator, basicTypes.anyTypeValidator); 19 | 20 | const ret = (target): target is Array => 21 | basicTypes.arrayOfAny(target) && target.every(typeValidator); 22 | setName(ret, `arrayOf(${typeValidator.name})`); 23 | ret._inner = typeValidator; 24 | return ret; 25 | } 26 | 27 | export function exactString(str: T): TypeValidator { 28 | assertType(str, basicTypes.string); 29 | 30 | const ret = (target): target is T => target === str; 31 | setName(ret, `exactString(${JSON.stringify(str)})`); 32 | ret._inner = str; 33 | return ret; 34 | } 35 | 36 | export function exactNumber(num: T): TypeValidator { 37 | assertType(num, basicTypes.numberIncludingNanAndInfinities); 38 | 39 | const ret = (target): target is T => target === num; 40 | setName(ret, `exactNumber(${num})`); 41 | ret._inner = num; 42 | return ret; 43 | } 44 | 45 | export function exactBigInt(num: T): TypeValidator { 46 | assertType(num, basicTypes.BigInt); 47 | 48 | const ret = (target): target is T => target === num; 49 | setName(ret, `exactBigInt(${num})`); 50 | ret._inner = num; 51 | return ret; 52 | } 53 | 54 | export function exactSymbol(sym: T): TypeValidator { 55 | assertType(sym, basicTypes.symbol); 56 | 57 | const ret = (target): target is T => target === sym; 58 | setName(ret, `exactSymbol(Symbol(${String(sym.description)}))`); 59 | ret._inner = sym; 60 | return ret; 61 | } 62 | 63 | export function hasClassName( 64 | name: Name, 65 | ): TypeValidator<{ constructor: Function & { name: Name } }> { 66 | assertType(name, basicTypes.string); 67 | 68 | const ret = (target): target is { constructor: Function & { name: Name } } => 69 | basicTypes.nonNullOrUndefined(target) && 70 | typeof target.constructor === "function" && 71 | target.constructor.name === name; 72 | setName(ret, `hasClassName(${JSON.stringify(name)})`); 73 | ret._inner = name; 74 | return ret; 75 | } 76 | 77 | export function hasToStringTag(name: string): TypeValidator { 78 | assertType(name, basicTypes.string); 79 | 80 | const expectedResult = `[object ${name}]`; 81 | const ret = (target: any): target is any => { 82 | return Object.prototype.toString.call(target) === expectedResult; 83 | }; 84 | setName(ret, `hasToStringTag(${JSON.stringify(name)})`); 85 | ret._inner = name; 86 | return ret; 87 | } 88 | 89 | export interface IntersectionFn { 90 | ( 91 | first: TypeValidator, 92 | second: TypeValidator, 93 | ): TypeValidator; 94 | 95 | ( 96 | first: TypeValidator, 97 | second: TypeValidator, 98 | third: TypeValidator, 99 | ): TypeValidator; 100 | 101 | ( 102 | first: TypeValidator, 103 | second: TypeValidator, 104 | third: TypeValidator, 105 | fourth: TypeValidator, 106 | ): TypeValidator; 107 | 108 | ( 109 | first: TypeValidator, 110 | second: TypeValidator, 111 | third: TypeValidator, 112 | fourth: TypeValidator, 113 | fifth: TypeValidator, 114 | ): TypeValidator; 115 | 116 | ( 117 | first: TypeValidator, 118 | second: TypeValidator, 119 | third: TypeValidator, 120 | fourth: TypeValidator, 121 | fifth: TypeValidator, 122 | sixth: TypeValidator, 123 | ): TypeValidator< 124 | FirstType & SecondType & ThirdType & FourthType & FifthType & SixthType 125 | >; 126 | 127 | < 128 | FirstType, 129 | SecondType, 130 | ThirdType, 131 | FourthType, 132 | FifthType, 133 | SixthType, 134 | SeventhType, 135 | >( 136 | first: TypeValidator, 137 | second: TypeValidator, 138 | third: TypeValidator, 139 | fourth: TypeValidator, 140 | fifth: TypeValidator, 141 | sixth: TypeValidator, 142 | seventh: TypeValidator, 143 | ): TypeValidator< 144 | FirstType & 145 | SecondType & 146 | ThirdType & 147 | FourthType & 148 | FifthType & 149 | SixthType & 150 | SeventhType 151 | >; 152 | 153 | < 154 | FirstType, 155 | SecondType, 156 | ThirdType, 157 | FourthType, 158 | FifthType, 159 | SixthType, 160 | SeventhType, 161 | EighthType, 162 | >( 163 | first: TypeValidator, 164 | second: TypeValidator, 165 | third: TypeValidator, 166 | fourth: TypeValidator, 167 | fifth: TypeValidator, 168 | sixth: TypeValidator, 169 | seventh: TypeValidator, 170 | eighth: TypeValidator, 171 | ): TypeValidator< 172 | FirstType & 173 | SecondType & 174 | ThirdType & 175 | FourthType & 176 | FifthType & 177 | SixthType & 178 | SeventhType & 179 | EighthType 180 | >; 181 | 182 | < 183 | FirstType, 184 | SecondType, 185 | ThirdType, 186 | FourthType, 187 | FifthType, 188 | SixthType, 189 | SeventhType, 190 | EighthType, 191 | NinthType, 192 | >( 193 | first: TypeValidator, 194 | second: TypeValidator, 195 | third: TypeValidator, 196 | fourth: TypeValidator, 197 | fifth: TypeValidator, 198 | sixth: TypeValidator, 199 | seventh: TypeValidator, 200 | eighth: TypeValidator, 201 | ninth: TypeValidator, 202 | ): TypeValidator< 203 | FirstType & 204 | SecondType & 205 | ThirdType & 206 | FourthType & 207 | FifthType & 208 | SixthType & 209 | SeventhType & 210 | EighthType & 211 | NinthType 212 | >; 213 | 214 | < 215 | FirstType, 216 | SecondType, 217 | ThirdType, 218 | FourthType, 219 | FifthType, 220 | SixthType, 221 | SeventhType, 222 | EighthType, 223 | NinthType, 224 | TenthType, 225 | >( 226 | first: TypeValidator, 227 | second: TypeValidator, 228 | third: TypeValidator, 229 | fourth: TypeValidator, 230 | fifth: TypeValidator, 231 | sixth: TypeValidator, 232 | seventh: TypeValidator, 233 | eighth: TypeValidator, 234 | ninth: TypeValidator, 235 | tenth: TypeValidator, 236 | ): TypeValidator< 237 | FirstType & 238 | SecondType & 239 | ThirdType & 240 | FourthType & 241 | FifthType & 242 | SixthType & 243 | SeventhType & 244 | EighthType & 245 | NinthType & 246 | TenthType 247 | >; 248 | } 249 | 250 | const arrayOfTypeValidator = arrayOf(basicTypes.anyTypeValidator); 251 | 252 | export const intersection: IntersectionFn = ( 253 | ...args: Array> 254 | ) => { 255 | assertType(args, arrayOfTypeValidator); 256 | 257 | const ret = (target: any): target is any => args.every((arg) => arg(target)); 258 | setName(ret, `intersection(${args.map((arg) => arg.name).join(", ")})`); 259 | ret._inner = args; 260 | return ret; 261 | }; 262 | 263 | export const and = intersection; 264 | 265 | export interface UnionFn { 266 | ( 267 | first: TypeValidator, 268 | second: TypeValidator, 269 | ): TypeValidator; 270 | 271 | ( 272 | first: TypeValidator, 273 | second: TypeValidator, 274 | third: TypeValidator, 275 | ): TypeValidator; 276 | 277 | ( 278 | first: TypeValidator, 279 | second: TypeValidator, 280 | third: TypeValidator, 281 | fourth: TypeValidator, 282 | ): TypeValidator; 283 | 284 | ( 285 | first: TypeValidator, 286 | second: TypeValidator, 287 | third: TypeValidator, 288 | fourth: TypeValidator, 289 | fifth: TypeValidator, 290 | ): TypeValidator; 291 | 292 | ( 293 | first: TypeValidator, 294 | second: TypeValidator, 295 | third: TypeValidator, 296 | fourth: TypeValidator, 297 | fifth: TypeValidator, 298 | sixth: TypeValidator, 299 | ): TypeValidator< 300 | FirstType | SecondType | ThirdType | FourthType | FifthType | SixthType 301 | >; 302 | 303 | < 304 | FirstType, 305 | SecondType, 306 | ThirdType, 307 | FourthType, 308 | FifthType, 309 | SixthType, 310 | SeventhType, 311 | >( 312 | first: TypeValidator, 313 | second: TypeValidator, 314 | third: TypeValidator, 315 | fourth: TypeValidator, 316 | fifth: TypeValidator, 317 | sixth: TypeValidator, 318 | seventh: TypeValidator, 319 | ): TypeValidator< 320 | | FirstType 321 | | SecondType 322 | | ThirdType 323 | | FourthType 324 | | FifthType 325 | | SixthType 326 | | SeventhType 327 | >; 328 | 329 | < 330 | FirstType, 331 | SecondType, 332 | ThirdType, 333 | FourthType, 334 | FifthType, 335 | SixthType, 336 | SeventhType, 337 | EighthType, 338 | >( 339 | first: TypeValidator, 340 | second: TypeValidator, 341 | third: TypeValidator, 342 | fourth: TypeValidator, 343 | fifth: TypeValidator, 344 | sixth: TypeValidator, 345 | seventh: TypeValidator, 346 | eighth: TypeValidator, 347 | ): TypeValidator< 348 | | FirstType 349 | | SecondType 350 | | ThirdType 351 | | FourthType 352 | | FifthType 353 | | SixthType 354 | | SeventhType 355 | | EighthType 356 | >; 357 | 358 | < 359 | FirstType, 360 | SecondType, 361 | ThirdType, 362 | FourthType, 363 | FifthType, 364 | SixthType, 365 | SeventhType, 366 | EighthType, 367 | NinthType, 368 | >( 369 | first: TypeValidator, 370 | second: TypeValidator, 371 | third: TypeValidator, 372 | fourth: TypeValidator, 373 | fifth: TypeValidator, 374 | sixth: TypeValidator, 375 | seventh: TypeValidator, 376 | eighth: TypeValidator, 377 | ninth: TypeValidator, 378 | ): TypeValidator< 379 | | FirstType 380 | | SecondType 381 | | ThirdType 382 | | FourthType 383 | | FifthType 384 | | SixthType 385 | | SeventhType 386 | | EighthType 387 | | NinthType 388 | >; 389 | 390 | < 391 | FirstType, 392 | SecondType, 393 | ThirdType, 394 | FourthType, 395 | FifthType, 396 | SixthType, 397 | SeventhType, 398 | EighthType, 399 | NinthType, 400 | TenthType, 401 | >( 402 | first: TypeValidator, 403 | second: TypeValidator, 404 | third: TypeValidator, 405 | fourth: TypeValidator, 406 | fifth: TypeValidator, 407 | sixth: TypeValidator, 408 | seventh: TypeValidator, 409 | eighth: TypeValidator, 410 | ninth: TypeValidator, 411 | tenth: TypeValidator, 412 | ): TypeValidator< 413 | | FirstType 414 | | SecondType 415 | | ThirdType 416 | | FourthType 417 | | FifthType 418 | | SixthType 419 | | SeventhType 420 | | EighthType 421 | | NinthType 422 | | TenthType 423 | >; 424 | } 425 | 426 | export const union: UnionFn = (...args: Array>) => { 427 | assertType(args, arrayOfTypeValidator); 428 | 429 | const ret = (target: any): target is any => args.some((arg) => arg(target)); 430 | setName(ret, `union(${args.map((arg) => arg.name).join(", ")})`); 431 | ret._inner = args; 432 | return ret; 433 | }; 434 | 435 | export const or = union; 436 | 437 | let thingThatCanHaveInstance: TypeValidator; 438 | if (typeof Symbol !== "undefined" && typeof Symbol.hasInstance === "symbol") { 439 | thingThatCanHaveInstance = or( 440 | basicTypes.anyFunction, 441 | objectWithProperties({ [Symbol.hasInstance]: basicTypes.anyFunction }), 442 | ); 443 | } else { 444 | thingThatCanHaveInstance = basicTypes.anyFunction; 445 | } 446 | 447 | export function instanceOf( 448 | klass: Klass, 449 | ): TypeValidator { 450 | assertType(klass, thingThatCanHaveInstance); 451 | 452 | const ret = (target): target is Klass => target instanceof klass; 453 | setName(ret, `instanceOf(${klass.name})`); 454 | ret._inner = klass; 455 | return ret; 456 | } 457 | 458 | export function mapOf( 459 | keyType: TypeValidator, 460 | valueType: TypeValidator, 461 | ): TypeValidator> { 462 | assertType(keyType, basicTypes.anyTypeValidator); 463 | assertType(valueType, basicTypes.anyTypeValidator); 464 | 465 | const ret = (target): target is Map => 466 | basicTypes.anyMap(target) && 467 | Array.from(target.keys()).every(keyType) && 468 | Array.from(target.values()).every(valueType); 469 | 470 | setName(ret, `mapOf(${keyType.name}, ${valueType.name})`); 471 | 472 | ret._inner = [keyType, valueType]; 473 | 474 | return ret; 475 | } 476 | 477 | export function setOf(itemType: TypeValidator): TypeValidator> { 478 | assertType(itemType, basicTypes.anyTypeValidator); 479 | 480 | const ret = (target): target is Set => 481 | basicTypes.anySet(target) && Array.from(target.values()).every(itemType); 482 | 483 | setName(ret, `setOf(${itemType.name})`); 484 | 485 | ret._inner = itemType; 486 | 487 | return ret; 488 | } 489 | 490 | export function maybe( 491 | itemType: TypeValidator, 492 | ): TypeValidator { 493 | assertType(itemType, basicTypes.anyTypeValidator); 494 | 495 | const ret = union(itemType, basicTypes.undefined, basicTypes.null); 496 | setName(ret, `maybe(${itemType.name})`); 497 | 498 | (ret as any)._inner = itemType; 499 | 500 | return ret; 501 | } 502 | 503 | export function optional( 504 | itemType: TypeValidator, 505 | ): TypeValidator { 506 | assertType(itemType, basicTypes.anyTypeValidator); 507 | 508 | const ret = union(itemType, basicTypes.undefined); 509 | setName(ret, `optional(${itemType.name})`); 510 | 511 | (ret as any)._inner = itemType; 512 | 513 | return ret; 514 | } 515 | 516 | export function objectWithProperties< 517 | T extends { [key: string | number | symbol]: TypeValidator }, 518 | >( 519 | properties: T, 520 | ): TypeValidator<{ 521 | [key in keyof T]: T[key] extends TypeValidator ? U : never; 522 | }> { 523 | assertType(properties, basicTypes.anyObject); 524 | 525 | for (const key in properties) { 526 | if (hasOwn(properties, key)) { 527 | assertType(properties[key], basicTypes.anyTypeValidator); 528 | } 529 | } 530 | 531 | const propEntries = allEntries(properties); 532 | 533 | const ret = ( 534 | target, 535 | ): target is { 536 | [key in keyof T]: T[key] extends TypeValidator ? U : never; 537 | } => { 538 | return ( 539 | basicTypes.anyObject(target) && 540 | propEntries.every(([name, validator]) => validator(target[name])) 541 | ); 542 | }; 543 | 544 | setName( 545 | ret, 546 | `objectWithProperties(${objectStr( 547 | Object.fromEntries(propEntries.map(([key, value]) => [key, value.name])), 548 | )})`, 549 | ); 550 | 551 | // We do this instead of just `ret._inner = properties` so mutations on the 552 | // original object don't carry across 553 | ret._inner = Object.fromEntries(propEntries); 554 | 555 | return ret; 556 | } 557 | 558 | export function objectWithOnlyTheseProperties< 559 | T extends { [key: string | number | symbol]: TypeValidator }, 560 | >( 561 | properties: T, 562 | ): TypeValidator<{ 563 | [key in keyof T]: T[key] extends TypeValidator ? U : never; 564 | }> { 565 | assertType(properties, basicTypes.anyObject); 566 | 567 | for (const key in properties) { 568 | if (hasOwn(properties, key)) { 569 | assertType(properties[key], basicTypes.anyTypeValidator); 570 | } 571 | } 572 | 573 | const propEntries = allEntries(properties); 574 | const propKeysSet = new Set(Reflect.ownKeys(properties)); 575 | 576 | const ret = ( 577 | target, 578 | ): target is { 579 | [key in keyof T]: T[key] extends TypeValidator ? U : never; 580 | } => { 581 | if (!basicTypes.anyObject(target)) return false; 582 | if (!propEntries.every(([name, validator]) => validator(target[name]))) 583 | return false; 584 | 585 | const targetEntries = allEntries(target); 586 | 587 | return ( 588 | targetEntries.length === propEntries.length && 589 | targetEntries.every(([key, _value]) => 590 | propKeysSet.has(typeof key === "number" ? String(key) : key), 591 | ) 592 | ); 593 | }; 594 | 595 | setName( 596 | ret, 597 | `objectWithOnlyTheseProperties(${objectStr( 598 | Object.fromEntries(propEntries.map(([key, value]) => [key, value.name])), 599 | )})`, 600 | ); 601 | 602 | // We do this instead of just `ret._inner = properties` so mutations on the 603 | // original object don't carry across 604 | ret._inner = Object.fromEntries(propEntries); 605 | 606 | return ret; 607 | } 608 | 609 | export function mappingObjectOf< 610 | Values, 611 | Keys extends string | number | symbol = string | number | symbol, 612 | >( 613 | keyType: TypeValidator, 614 | valueType: TypeValidator, 615 | ): TypeValidator> { 616 | assertType(keyType, basicTypes.anyTypeValidator); 617 | assertType(valueType, basicTypes.anyTypeValidator); 618 | 619 | const ret = (target): target is Record => 620 | basicTypes.anyObject(target) && 621 | allEntries(target).every( 622 | ([key, value]) => keyType(key) && valueType(value), 623 | ); 624 | setName(ret, `mappingObjectOf(${keyType.name}, ${valueType.name})`); 625 | ret._inner = [keyType, valueType]; 626 | return ret; 627 | } 628 | 629 | export const record = mappingObjectOf; 630 | 631 | export function partialObjectWithProperties< 632 | T extends { [key: string | number | symbol]: TypeValidator }, 633 | >( 634 | properties: T, 635 | ): TypeValidator<{ 636 | [key in keyof T]: T[key] extends TypeValidator 637 | ? U | null | undefined 638 | : never; 639 | }> { 640 | assertType(properties, basicTypes.anyObject); 641 | 642 | for (const key in properties) { 643 | if (hasOwn(properties, key)) { 644 | assertType(properties[key], basicTypes.anyTypeValidator); 645 | } 646 | } 647 | 648 | const propEntries = allEntries(properties); 649 | 650 | const ret = ( 651 | target, 652 | ): target is { 653 | [key in keyof T]: T[key] extends TypeValidator 654 | ? U | null | undefined 655 | : never; 656 | } => { 657 | return ( 658 | basicTypes.anyObject(target) && 659 | propEntries.every( 660 | ([name, validator]) => target[name] == null || validator(target[name]), 661 | ) 662 | ); 663 | }; 664 | 665 | setName( 666 | ret, 667 | `partialObjectWithProperties(${objectStr( 668 | Object.fromEntries( 669 | propEntries.map(([key, value]) => [ 670 | key, 671 | `union(${value.name}, null, undefined)`, 672 | ]), 673 | ), 674 | )})`, 675 | ); 676 | 677 | // We do this instead of just `ret._inner = properties` so mutations on the 678 | // original object don't carry across 679 | ret._inner = Object.fromEntries(propEntries); 680 | 681 | return ret; 682 | } 683 | 684 | export function stringMatching(regexp: RegExp): TypeValidator { 685 | assertType(regexp, basicTypes.RegExp); 686 | 687 | const ret = (target): target is string => { 688 | // We make a new regexp each time so that state from one match doesn't affect another 689 | const newRegExp = new RegExp(regexp.source, regexp.flags); 690 | return typeof target === "string" && newRegExp.test(target); 691 | }; 692 | setName(ret, `stringMatching(${regexp})`); 693 | ret._inner = regexp; 694 | return ret; 695 | } 696 | 697 | export function symbolFor(key: string): TypeValidator { 698 | assertType(key, basicTypes.string); 699 | 700 | const ret = (target): target is symbol => target === Symbol.for(key); 701 | setName(ret, `symbolFor(${JSON.stringify(key)})`); 702 | ret._inner = key; 703 | return ret; 704 | } 705 | 706 | export interface TupleFn { 707 | ( 708 | first: TypeValidator, 709 | second: TypeValidator, 710 | ): TypeValidator<[FirstType, SecondType]>; 711 | 712 | ( 713 | first: TypeValidator, 714 | second: TypeValidator, 715 | third: TypeValidator, 716 | ): TypeValidator<[FirstType, SecondType, ThirdType]>; 717 | 718 | ( 719 | first: TypeValidator, 720 | second: TypeValidator, 721 | third: TypeValidator, 722 | fourth: TypeValidator, 723 | ): TypeValidator<[FirstType, SecondType, ThirdType, FourthType]>; 724 | 725 | ( 726 | first: TypeValidator, 727 | second: TypeValidator, 728 | third: TypeValidator, 729 | fourth: TypeValidator, 730 | fifth: TypeValidator, 731 | ): TypeValidator<[FirstType, SecondType, ThirdType, FourthType, FifthType]>; 732 | 733 | ( 734 | first: TypeValidator, 735 | second: TypeValidator, 736 | third: TypeValidator, 737 | fourth: TypeValidator, 738 | fifth: TypeValidator, 739 | sixth: TypeValidator, 740 | ): TypeValidator< 741 | [FirstType, SecondType, ThirdType, FourthType, FifthType, SixthType] 742 | >; 743 | 744 | < 745 | FirstType, 746 | SecondType, 747 | ThirdType, 748 | FourthType, 749 | FifthType, 750 | SixthType, 751 | SeventhType, 752 | >( 753 | first: TypeValidator, 754 | second: TypeValidator, 755 | third: TypeValidator, 756 | fourth: TypeValidator, 757 | fifth: TypeValidator, 758 | sixth: TypeValidator, 759 | seventh: TypeValidator, 760 | ): TypeValidator< 761 | [ 762 | FirstType, 763 | SecondType, 764 | ThirdType, 765 | FourthType, 766 | FifthType, 767 | SixthType, 768 | SeventhType, 769 | ] 770 | >; 771 | 772 | < 773 | FirstType, 774 | SecondType, 775 | ThirdType, 776 | FourthType, 777 | FifthType, 778 | SixthType, 779 | SeventhType, 780 | EighthType, 781 | >( 782 | first: TypeValidator, 783 | second: TypeValidator, 784 | third: TypeValidator, 785 | fourth: TypeValidator, 786 | fifth: TypeValidator, 787 | sixth: TypeValidator, 788 | seventh: TypeValidator, 789 | eighth: TypeValidator, 790 | ): TypeValidator< 791 | [ 792 | FirstType, 793 | SecondType, 794 | ThirdType, 795 | FourthType, 796 | FifthType, 797 | SixthType, 798 | SeventhType, 799 | EighthType, 800 | ] 801 | >; 802 | 803 | < 804 | FirstType, 805 | SecondType, 806 | ThirdType, 807 | FourthType, 808 | FifthType, 809 | SixthType, 810 | SeventhType, 811 | EighthType, 812 | NinthType, 813 | >( 814 | first: TypeValidator, 815 | second: TypeValidator, 816 | third: TypeValidator, 817 | fourth: TypeValidator, 818 | fifth: TypeValidator, 819 | sixth: TypeValidator, 820 | seventh: TypeValidator, 821 | eighth: TypeValidator, 822 | ninth: TypeValidator, 823 | ): TypeValidator< 824 | [ 825 | FirstType, 826 | SecondType, 827 | ThirdType, 828 | FourthType, 829 | FifthType, 830 | SixthType, 831 | SeventhType, 832 | EighthType, 833 | NinthType, 834 | ] 835 | >; 836 | 837 | < 838 | FirstType, 839 | SecondType, 840 | ThirdType, 841 | FourthType, 842 | FifthType, 843 | SixthType, 844 | SeventhType, 845 | EighthType, 846 | NinthType, 847 | TenthType, 848 | >( 849 | first: TypeValidator, 850 | second: TypeValidator, 851 | third: TypeValidator, 852 | fourth: TypeValidator, 853 | fifth: TypeValidator, 854 | sixth: TypeValidator, 855 | seventh: TypeValidator, 856 | eighth: TypeValidator, 857 | ninth: TypeValidator, 858 | tenth: TypeValidator, 859 | ): TypeValidator< 860 | [ 861 | FirstType, 862 | SecondType, 863 | ThirdType, 864 | FourthType, 865 | FifthType, 866 | SixthType, 867 | SeventhType, 868 | EighthType, 869 | NinthType, 870 | TenthType, 871 | ] 872 | >; 873 | } 874 | 875 | export const tuple: TupleFn = (...args: Array>) => { 876 | assertType(args, arrayOfTypeValidator); 877 | 878 | const ret = (target: any): target is any => 879 | basicTypes.anyArray(target) && 880 | target.length === args.length && 881 | target.every((item, index) => { 882 | return args[index](item); 883 | }); 884 | 885 | setName(ret, `tuple(${args.map((arg) => arg.name).join(", ")})`); 886 | 887 | ret._inner = args; 888 | 889 | return ret; 890 | }; 891 | -------------------------------------------------------------------------------- /src/type-validator.ts: -------------------------------------------------------------------------------- 1 | export type TypeValidator = (value: any) => value is T; 2 | 3 | export type ExtractTypeFromValidator> = 4 | Validator extends TypeValidator ? R : never; 5 | -------------------------------------------------------------------------------- /src/utils.ts: -------------------------------------------------------------------------------- 1 | export function setName(fn: Function, name: string) { 2 | Object.defineProperty(fn, "name", { value: name }); 3 | } 4 | 5 | export function isTagged(target: any, tag: string): boolean { 6 | return Object.prototype.toString.call(target) === `[object ${tag}]`; 7 | } 8 | 9 | export const hasOwn = (target: object, prop: string | number | symbol) => 10 | Object.prototype.hasOwnProperty.call(target, prop); 11 | 12 | export const allEntries = (target: object): Array<[PropertyKey, any]> => { 13 | const keys = Reflect.ownKeys(target); 14 | 15 | const results: Array<[PropertyKey, any]> = []; 16 | 17 | for (const key of keys) { 18 | const value = target[key]; 19 | results.push([key, value]); 20 | } 21 | 22 | return results; 23 | }; 24 | -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "include": ["./src/**/*.ts"], 3 | "exclude": ["./**/*.test.ts"], 4 | 5 | "compilerOptions": { 6 | "lib": ["es2020", "dom"], 7 | "target": "es2020", 8 | "module": "CommonJS", 9 | "outDir": "./dist", 10 | "declaration": true, 11 | 12 | "strict": true, 13 | "noImplicitAny": false, 14 | "strictNullChecks": true, 15 | "strictFunctionTypes": true, 16 | "strictPropertyInitialization": true, 17 | "noImplicitThis": true, 18 | "alwaysStrict": true, 19 | "noUnusedLocals": false, 20 | "noUnusedParameters": false, 21 | "noImplicitReturns": true, 22 | "noFallthroughCasesInSwitch": true, 23 | "downlevelIteration": true, 24 | 25 | "moduleResolution": "node", 26 | "esModuleInterop": true, 27 | }, 28 | } 29 | --------------------------------------------------------------------------------