├── test ├── index.js ├── types │ ├── props │ │ ├── index.js │ │ ├── base.js │ │ ├── inexact.js │ │ └── exact.js │ ├── index.js │ ├── refinement.js │ ├── literal.js │ ├── keyof.js │ ├── core.js │ ├── intersection.js │ ├── tuple.js │ ├── array.js │ ├── dictionary.js │ ├── union.js │ └── basic.js ├── helpers.js └── fp │ └── index.js ├── src ├── index.js ├── Reporter.js ├── types │ ├── props │ │ ├── index.js │ │ ├── exact.js │ │ ├── inexact.js │ │ └── base.js │ ├── keyof.js │ ├── index.js │ ├── literal.js │ ├── refinement.js │ ├── array.js │ ├── basic.js │ ├── intersection.js │ ├── dictionary.js │ ├── tuple.js │ ├── union.js │ └── core.js └── fp │ └── index.js ├── docs ├── .DS_Store └── images │ ├── inference.png │ └── introspection.png ├── .gitignore ├── .flowconfig ├── .eslintrc ├── .babelrc ├── LICENSE ├── CHANGELOG.md ├── .circleci └── config.yml ├── package.json ├── flow-typed └── npm │ ├── unzipper_vx.x.x.js │ ├── got_vx.x.x.js │ ├── @octokit │ └── rest_vx.x.x.js │ ├── semver_v5.1.x.js │ ├── flow-typed_vx.x.x.js │ ├── bluebird_v3.x.x.js │ └── jest_v22.x.x.js ├── scripts └── test.js └── README.md /test/index.js: -------------------------------------------------------------------------------- 1 | //@flow 2 | import './types'; 3 | import './fp'; 4 | -------------------------------------------------------------------------------- /src/index.js: -------------------------------------------------------------------------------- 1 | //@flow 2 | export * from './fp'; 3 | export * from './types'; 4 | -------------------------------------------------------------------------------- /docs/.DS_Store: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/orlandoc01/io-flow-types/HEAD/docs/.DS_Store -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | *.log 2 | node_modules 3 | lib 4 | dev 5 | coverage 6 | scripts/.flow-bins-cache/* 7 | -------------------------------------------------------------------------------- /docs/images/inference.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/orlandoc01/io-flow-types/HEAD/docs/images/inference.png -------------------------------------------------------------------------------- /test/types/props/index.js: -------------------------------------------------------------------------------- 1 | //@flow 2 | import './base.js'; 3 | import './exact.js'; 4 | import './inexact.js'; 5 | -------------------------------------------------------------------------------- /docs/images/introspection.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/orlandoc01/io-flow-types/HEAD/docs/images/introspection.png -------------------------------------------------------------------------------- /src/Reporter.js: -------------------------------------------------------------------------------- 1 | //@flow 2 | import type { Validation } from './index'; 3 | 4 | export interface Reporter { 5 | report: (validation: Validation) => A; 6 | } 7 | -------------------------------------------------------------------------------- /.flowconfig: -------------------------------------------------------------------------------- 1 | [ignore] 2 | .*/node_modules/.* 3 | /lib/.* 4 | 5 | [libs] 6 | ./flow-typed/* 7 | 8 | [options] 9 | suppress_comment= \\(.\\|\n\\)*\\$FlowExpectError 10 | include_warnings=true 11 | -------------------------------------------------------------------------------- /src/types/props/index.js: -------------------------------------------------------------------------------- 1 | //@flow 2 | export type { AnyProps, Props } from './base.js'; 3 | export { empty } from './base.js'; 4 | export { exact, exactAll, exactShape } from './exact.js'; 5 | export { inexact, inexactAll, inexactShape } from './inexact.js'; 6 | -------------------------------------------------------------------------------- /test/types/index.js: -------------------------------------------------------------------------------- 1 | //@flow 2 | import './props'; 3 | import './array'; 4 | import './core'; 5 | import './basic'; 6 | import './dictionary'; 7 | import './intersection'; 8 | import './keyof'; 9 | import './literal'; 10 | import './refinement'; 11 | import './tuple'; 12 | import './union'; 13 | -------------------------------------------------------------------------------- /src/types/keyof.js: -------------------------------------------------------------------------------- 1 | //@flow 2 | // 3 | // keyof 4 | // 5 | import { Type, String } from './index.js'; 6 | 7 | export const keyof = ( 8 | keys: D, 9 | name?: string = `(keyof ${JSON.stringify(Object.keys(keys))})` 10 | ): Type<$Keys> => { 11 | const is = (m: mixed) => String.is(m) && keys.hasOwnProperty(m); 12 | return new Type(name, is); 13 | }; 14 | -------------------------------------------------------------------------------- /src/types/index.js: -------------------------------------------------------------------------------- 1 | //@flow 2 | export * from './core.js'; 3 | export * from './basic.js'; 4 | export * from './array.js'; 5 | export * from './dictionary.js'; 6 | export * from './intersection.js'; 7 | export * from './keyof.js'; 8 | export * from './literal.js'; 9 | export * from './union.js'; 10 | export * from './tuple.js'; 11 | export * from './props/index.js'; 12 | export * from './refinement.js'; 13 | -------------------------------------------------------------------------------- /src/types/literal.js: -------------------------------------------------------------------------------- 1 | //@flow 2 | // 3 | // literals 4 | // 5 | import { Type, getDefaultContext, success, failure, identity } from './index.js'; 6 | 7 | export const literal = (value: V, name?: string = JSON.stringify(value)): Type => { 8 | const is = (m: mixed) => m === value; 9 | return new Type(name, is, (m, c) => (is(m) ? success(value) : failure(m, c)), identity); 10 | }; 11 | -------------------------------------------------------------------------------- /.eslintrc: -------------------------------------------------------------------------------- 1 | { 2 | "parser": "babel-eslint", 3 | "extends": [ 4 | "eslint:recommended", 5 | "plugin:flowtype/recommended" 6 | ], 7 | "plugins": [ 8 | "flowtype" 9 | ], 10 | "env": { 11 | "jest": true, 12 | "node": true, 13 | "es6": true 14 | }, 15 | "rules": { 16 | "no-unused-vars": 0, 17 | "space-before-generic-bracket": 0, 18 | "flowtype/generic-spacing": 0 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /.babelrc: -------------------------------------------------------------------------------- 1 | { 2 | "presets": [ 3 | "@babel/preset-flow", 4 | [ 5 | "@babel/preset-env", 6 | { 7 | "debug": false, 8 | "targets": { 9 | "browsers": [ 10 | "last 3 versions" 11 | ] 12 | } 13 | } 14 | ] 15 | ], 16 | "plugins": ["@babel/plugin-proposal-class-properties"] 17 | } 18 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2019 Orlando Castillo 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # Changelog 2 | 3 | **Note**: Gaps between patch versions are faulty/broken releases. **Note**: A feature tagged as Experimental is in a 4 | high state of flux, you're at risk of it changing without notice. 5 | 6 | ## 0.2.1 7 | ### Added 8 | - Add new `ValidationError` class with default error message using `PathReporter` logic 9 | - Add new `AggregateError` class for error handling multiple `ValidationError` instances 10 | ### Changed 11 | - `Validation` is now `Either` 12 | * `AggregateError` is subclass of `Array`, so should be backwards compatible 13 | - Add `assert` method on `Type` class 14 | - Add `getAssert` method on `Type` class 15 | - `toString` will now render the name for function or a default string with its arity 16 | - Restructured tests directory 17 | - Add optional `message` arg to `failure` 18 | ### Removed 19 | - remove `PathReporter` - `ValidationError` message uses path logic for message 20 | - remove `ThrowReporter` - added `assert` method mimicks the logic 21 | - remove `getFunctionName` export 22 | - remove `getContextEntry` export 23 | - remove `getValidationError` export 24 | 25 | ## 0.1.3 26 | ### Fixed 27 | - Patches type error emerging after 0.95.1 for toString 28 | 29 | ## 0.1.2 30 | ### Fixed 31 | - Patches flow-bin peer dependency range to avoid JSON.stringify conflict in 0.95.0 release of flow 32 | 33 | 34 | ## 0.1.1 35 | Initial release 36 | -------------------------------------------------------------------------------- /src/types/refinement.js: -------------------------------------------------------------------------------- 1 | //@flow 2 | import { isLeft, getFunctionName } from '../fp'; 3 | import { Type, success, failure, Number } from './index.js'; 4 | import type { Predicate, AnyFlowType } from './index.js'; 5 | 6 | export function refinement( 7 | type: RT, 8 | predicate: Predicate<$PropertyType>, 9 | name: string = `(${type.name} | ${getFunctionName(predicate)})` 10 | ): Type<$PropertyType, $PropertyType, $PropertyType> { 11 | return new Type( 12 | name, 13 | m => type.is(m) && predicate(m), 14 | (i, c) => { 15 | const validation = type.validate(i, c); 16 | if (validation.isLeft()) return validation; 17 | const a = validation.value; 18 | return predicate(a) ? success((a: any)) : failure(a, c); 19 | }, 20 | type.encode 21 | ); 22 | } 23 | 24 | /** Special refinement that allows users to supply an Opaque Type as the A parameter of the Type class */ 25 | export function opaqueRefine>( 26 | type: RT, 27 | predicate: Predicate<$PropertyType>, 28 | name?: string 29 | ): Type, $PropertyType> { 30 | const refined = refinement(type, predicate, name); 31 | return refined; 32 | } 33 | export opaque type Int: number = number; 34 | export const Integer = opaqueRefine(Number, v => v % 1 === 0, 'Integer'); 35 | -------------------------------------------------------------------------------- /test/types/refinement.js: -------------------------------------------------------------------------------- 1 | //@flow 2 | import * as assert from 'assert'; 3 | import * as t from '../../src/index'; 4 | import { assertSuccess, assertFailure, assertStrictEqual, DateFromNumber, IntegerFromString } from '../helpers'; 5 | 6 | describe('refinement', () => { 7 | it('should return the same reference if validation succeeded', () => { 8 | const T = t.refinement(t.Dictionary, () => true); 9 | const value = {}; 10 | assertStrictEqual(T.decode(value), value); 11 | }); 12 | 13 | it('should fail validating an invalid value', () => { 14 | const T = t.Integer; 15 | assertFailure(T.decode('a'), ['Invalid value "a" supplied to : Integer']); 16 | assertFailure(T.decode(1.2), ['Invalid value 1.2 supplied to : Integer']); 17 | }); 18 | 19 | it('should fail with the last deserialized value', () => { 20 | const T = IntegerFromString; 21 | assertFailure(T.decode('a'), ['Invalid value "a" supplied to : IntegerFromString']); 22 | assertFailure(T.decode('1.2'), ['Invalid value 1.2 supplied to : IntegerFromString']); 23 | }); 24 | 25 | it('should serialize a deserialized', () => { 26 | const T = t.refinement(t.array(DateFromNumber), () => true); 27 | assert.deepEqual(T.encode([new Date(0)]), [0]); 28 | }); 29 | 30 | it('should return the same reference when serializing', () => { 31 | const T = t.refinement(t.array(t.Number), () => true); 32 | assert.strictEqual(T.encode, t.identity); 33 | }); 34 | 35 | it('should type guard', () => { 36 | const T = t.Integer; 37 | assert.strictEqual(T.is(1.2), false); 38 | assert.strictEqual(T.is('a'), false); 39 | assert.strictEqual(T.is(1), true); 40 | }); 41 | }); 42 | -------------------------------------------------------------------------------- /test/types/literal.js: -------------------------------------------------------------------------------- 1 | //@flow 2 | import * as assert from 'assert'; 3 | import * as t from '../../src/index'; 4 | import { assertSuccess, assertFailure } from '../helpers'; 5 | 6 | describe('literal', () => { 7 | it('should properly typecheck values', () => { 8 | const T = t.literal<'foo'>('foo'); 9 | const a: t.TypeOf = 'foo'; 10 | const b: t.OutputOf = 'foo'; 11 | // $FlowExpectError 12 | const c: t.TypeOf = 'foo1'; 13 | // $FlowExpectError 14 | const d: t.TypeOf = 1; 15 | // $FlowExpectError 16 | const e: t.OutputOf = 'foo1'; 17 | // $FlowExpectError 18 | const f: t.OutputOf = 1; 19 | }); 20 | 21 | it('should succeed validating a valid value', () => { 22 | const T = t.literal<'a'>('a'); 23 | assertSuccess(T.decode('a')); 24 | }); 25 | 26 | it('should fail validating an invalid value', () => { 27 | const T = t.literal<'a'>('a'); 28 | assertFailure(T.decode(1), ['Invalid value 1 supplied to : "a"']); 29 | }); 30 | 31 | it('should return the same reference when serializing', () => { 32 | const T = t.literal<'a'>('a'); 33 | assert.strictEqual(T.encode, t.identity); 34 | }); 35 | 36 | it('should type guard', () => { 37 | const T = t.literal<'a'>('a'); 38 | assert.strictEqual(T.is('a'), true); 39 | assert.strictEqual(T.is('b'), false); 40 | assert.strictEqual(T.is(undefined), false); 41 | }); 42 | 43 | it('should assign a default name', () => { 44 | const T1 = t.literal<'a'>('a'); 45 | assert.strictEqual(T1.name, '"a"'); 46 | const T2 = t.literal<'a'>('a', 'T2'); 47 | assert.strictEqual(T2.name, 'T2'); 48 | }); 49 | }); 50 | -------------------------------------------------------------------------------- /.circleci/config.yml: -------------------------------------------------------------------------------- 1 | docker_image: &docker_image 2 | working_directory: ~/repo 3 | docker: 4 | - image: circleci/node:10 5 | 6 | version: 2 7 | jobs: 8 | test: 9 | <<: *docker_image 10 | environment: 11 | BATCH_SIZE: 1 12 | steps: 13 | - checkout 14 | # Download and cache dependencies 15 | - restore_cache: 16 | keys: 17 | - v1-dependencies-{{ checksum "package.json" }}-{{ checksum "package-lock.json" }} 18 | - run: npm ci 19 | - save_cache: 20 | paths: 21 | - node_modules 22 | key: v1-dependencies-{{ checksum "package.json" }}-{{ checksum "package-lock.json" }} 23 | # run tests! 24 | - run: npm run lint 25 | - run: npm run prettier 26 | - run: npm run jest 27 | - run: npm run flow-regression 28 | 29 | deploy: 30 | <<: *docker_image 31 | steps: 32 | - checkout 33 | # Download and cache dependencies 34 | - restore_cache: 35 | keys: 36 | - v1-dependencies-{{ checksum "package.json" }}-{{ checksum "package-lock.json" }} 37 | - run: 38 | name: Authenticate with registry 39 | command: echo "//registry.npmjs.org/:_authToken=$NPM_TOKEN" > ~/repo/.npmrc 40 | - run: 41 | name: Publish package 42 | command: npm publish 43 | 44 | workflows: 45 | version: 2 46 | io-flow-types-workflow: 47 | jobs: 48 | - test: 49 | filters: 50 | tags: 51 | only: /^v.*/ 52 | - deploy: 53 | requires: 54 | - test 55 | filters: 56 | tags: 57 | only: /^v.*/ 58 | branches: 59 | ignore: /.*/ 60 | -------------------------------------------------------------------------------- /test/types/keyof.js: -------------------------------------------------------------------------------- 1 | //@flow 2 | import * as assert from 'assert'; 3 | import * as t from '../../src/index'; 4 | import { assertSuccess, assertFailure } from '../helpers'; 5 | 6 | describe('keyof', () => { 7 | it('should properly typecheck values', () => { 8 | const T = t.keyof({ foo: true, bar: true }); 9 | const a: t.TypeOf = 'foo'; 10 | const b: t.OutputOf = 'bar'; 11 | // $FlowExpectError 12 | const c: t.TypeOf = 'foo1'; 13 | // $FlowExpectError 14 | const e: t.OutputOf = 'bar2'; 15 | // $FlowExpectError 16 | const f: t.OutputOf = 1; 17 | }); 18 | it('should succeed validating a valid value', () => { 19 | const T = t.keyof({ a: 1, b: 2 }); 20 | assertSuccess(T.decode('a')); 21 | assertSuccess(T.decode('b')); 22 | }); 23 | 24 | it('should fail validating an invalid value', () => { 25 | const T = t.keyof({ a: 1, b: 2 }); 26 | assertFailure(T.decode('c'), ['Invalid value "c" supplied to : (keyof ["a","b"])']); 27 | }); 28 | 29 | it('should return the same reference when serializing', () => { 30 | const T = t.keyof({ a: 1, b: 2 }); 31 | assert.strictEqual(T.encode, t.identity); 32 | }); 33 | 34 | it('should type guard', () => { 35 | const T = t.keyof({ a: 1, b: 2 }); 36 | assert.strictEqual(T.is('a'), true); 37 | assert.strictEqual(T.is('c'), false); 38 | assert.strictEqual(T.is(undefined), false); 39 | }); 40 | 41 | it('should assign a default name', () => { 42 | const T1 = t.keyof({ a: 1, b: 2 }); 43 | assert.strictEqual(T1.name, '(keyof ["a","b"])'); 44 | const T2 = t.keyof({ a: 1, b: 2 }, 'T2'); 45 | assert.strictEqual(T2.name, 'T2'); 46 | }); 47 | }); 48 | -------------------------------------------------------------------------------- /src/types/array.js: -------------------------------------------------------------------------------- 1 | //@flow 2 | // 3 | // arrays 4 | // 5 | import { isLeft } from '../fp'; 6 | import { Type, success, failures, identity, arrayType, isArray, appendContext, AggregateError } from './index.js'; 7 | import type { MixedFlowType, TypeOf, OutputOf } from './index.js'; 8 | 9 | export function array( 10 | type: RT, 11 | name: string = `Array<${type.name}>` 12 | ): Type>, Array<$PropertyType>, mixed> { 13 | const is = m => isArray(m) && m.every(type.is); 14 | const encode = type.encode === identity ? identity : a => a.map(type.encode); 15 | return new Type(name, is, validate, encode); 16 | 17 | function validate(m, c) { 18 | const arrayValidation = arrayType.validate(m, c); 19 | if (isLeft(arrayValidation)) { 20 | return arrayValidation; 21 | } 22 | const xs = arrayValidation.value; 23 | const errors = new AggregateError(); 24 | const a: Array> = xs.reduce(validateValue, xs); 25 | return errors.length ? failures(errors) : success(a); 26 | 27 | function validateValue(acc, elem, index: number) { 28 | const validation = type.validate(elem, appendContext(c, String(index), type)); 29 | if (isLeft(validation)) { 30 | errors.push(...validation.value); 31 | return acc; 32 | } 33 | const vok = validation.value; 34 | const createNewArr = vok !== elem && acc === xs; 35 | const updatedAcc = createNewArr ? [...xs] : acc; 36 | updatedAcc[index] = vok; 37 | return updatedAcc; 38 | } 39 | } 40 | } 41 | 42 | export function readonlyArray( 43 | type: RT, 44 | name: string = `$ReadOnlyArray<${type.name}>` 45 | ): Type<$ReadOnlyArray<$PropertyType>, $ReadOnlyArray<$PropertyType>, mixed> { 46 | const arrayType = array(type); 47 | const is: mixed => boolean = arrayType.is; 48 | const isNotProduction = process.env.NODE_ENV !== 'production'; 49 | return new Type( 50 | name, 51 | is, 52 | (m, c) => { 53 | const validation = arrayType.validate(m, c); 54 | return validation.map(x => (isNotProduction ? Object.freeze(x) : x)); 55 | }, 56 | type.encode === identity ? identity : a => a.map(type.encode) 57 | ); 58 | } 59 | -------------------------------------------------------------------------------- /test/types/props/base.js: -------------------------------------------------------------------------------- 1 | //@flow 2 | import * as assert from 'assert'; 3 | import * as t from '../../../src/index'; 4 | import { assertSuccess, assertFailure, DateFromNumber } from '../../helpers'; 5 | 6 | describe('readonly', () => { 7 | it('should succeed validating a valid value', () => { 8 | const T = t.exactAll({ a: t.Number }).readOnly(); 9 | assertSuccess(T.decode({ a: 1 })); 10 | }); 11 | 12 | it('should fail validating an invalid value', () => { 13 | const T = t.exactAll({ a: t.Number }).readOnly(); 14 | assertFailure(T.decode({}), ['Invalid value undefined supplied to : {| +a: number |}/a: number']); 15 | }); 16 | 17 | it('should freeze the value', () => { 18 | const T = t.exactAll({ a: t.Number }).readOnly(); 19 | T.decode({ a: 1 }).map(x => assert.ok(Object.isFrozen(x))); 20 | }); 21 | 22 | it('should not freeze in production', () => { 23 | const env = process.env.NODE_ENV; 24 | process.env.NODE_ENV = 'production'; 25 | const T = t.exactAll({ a: t.Number }).readOnly(); 26 | T.decode({ a: 1 }).map(x => assert.ok(!Object.isFrozen(x))); 27 | process.env.NODE_ENV = env; 28 | }); 29 | 30 | it('should serialize a deserialized', () => { 31 | const T = t.exactAll({ a: DateFromNumber }).readOnly(); 32 | assert.deepEqual(T.encode({ a: new Date(0) }), { a: 0 }); 33 | }); 34 | 35 | it('should return the same reference when serializing', () => { 36 | const T = t.exactAll({ a: t.Number }).readOnly(); 37 | assert.strictEqual(T.encode, t.identity); 38 | }); 39 | 40 | it('should type guard', () => { 41 | const T1 = t.exactAll({ a: t.Number }).readOnly(); 42 | assert.strictEqual(T1.is({ a: 1 }), true); 43 | assert.strictEqual(T1.is({ a: 'foo' }), false); 44 | assert.strictEqual(T1.is(undefined), false); 45 | const T2 = t.exactAll({ a: DateFromNumber }).readOnly(); 46 | assert.strictEqual(T2.is({ a: new Date(0) }), true); 47 | assert.strictEqual(T2.is({ a: 0 }), false); 48 | assert.strictEqual(T2.is(undefined), false); 49 | }); 50 | 51 | it('should assign a default name', () => { 52 | const T1 = t.exactAll({ a: t.Number }).readOnly(); 53 | assert.strictEqual(T1.name, '{| +a: number |}'); 54 | const T2 = t.exactAll({ a: t.Number }).readOnly('T2'); 55 | assert.strictEqual(T2.name, 'T2'); 56 | }); 57 | }); 58 | -------------------------------------------------------------------------------- /src/types/basic.js: -------------------------------------------------------------------------------- 1 | //@flow 2 | // 3 | // basic types 4 | // 5 | import { Type, getDefaultContext, success, failure, identity } from './index.js'; 6 | 7 | export const isArray = (arr: mixed): boolean %checks => Array.isArray(arr); 8 | export const isObject = (obj: mixed): boolean %checks => typeof obj === 'object' && obj !== null; 9 | export const isDictionary = (obj: mixed): boolean %checks => 10 | isObject(obj) && Object.prototype.toString.call(obj) === '[object Object]'; 11 | 12 | /** @alias `null` */ 13 | export opaque type NullType: Type = Type; 14 | export const Null: NullType = new Type('null', n => n === null); 15 | 16 | export opaque type UndefinedType: Type = Type; 17 | export const Undefined: UndefinedType = new Type('undefined', n => n === void 0); 18 | 19 | export opaque type VoidType: Type = Type; 20 | export const Void: VoidType = new Type('void', Undefined.is, Undefined.validate); 21 | 22 | export opaque type AnyType: Type = Type; 23 | export const Any: AnyType = new Type('any', () => true, success); 24 | 25 | export opaque type StringType: Type = Type; 26 | export const String: StringType = new Type('string', m => typeof m === 'string'); 27 | 28 | export opaque type NumberType: Type = Type; 29 | export const Number: NumberType = new Type('number', m => typeof m === 'number'); 30 | 31 | export opaque type BooleanType: Type = Type; 32 | export const Boolean: BooleanType = new Type('boolean', m => typeof m === 'boolean'); 33 | 34 | export opaque type MixedArrayType: Type> = Type>; 35 | export const arrayType: MixedArrayType = new Type('Array', isArray); 36 | 37 | export opaque type MixedDictionaryType: Type<{ [key: string]: mixed }> = Type<{ [key: string]: mixed }>; 38 | export const Dictionary: MixedDictionaryType = new Type('Dictionary', isDictionary); 39 | 40 | export opaque type ObjectType: Type = Type; 41 | export const object: ObjectType = new Type('Object', isObject); 42 | 43 | export opaque type never = mixed; 44 | export opaque type NeverType: Type = Type; 45 | export const Never: NeverType = new Type( 46 | 'never', 47 | () => false, 48 | (m, c) => failure(m, c), 49 | () => { 50 | throw new Error('cannot encode never'); 51 | } 52 | ); 53 | -------------------------------------------------------------------------------- /test/helpers.js: -------------------------------------------------------------------------------- 1 | //@flow 2 | import * as assert from 'assert'; 3 | import * as t from '../src/index'; 4 | 5 | export function assertSuccess(validation: t.Validation): void { 6 | assert.ok(validation.isRight()); 7 | } 8 | 9 | export function assertFailure(validation: t.Validation, descriptions: Array): void { 10 | assert.ok(validation.isLeft()); 11 | if (validation.tag === 'Left') { 12 | assert.deepEqual(validation.value.messages(), descriptions); 13 | } 14 | } 15 | 16 | export function assertStrictEqual(validation: t.Validation, value: T): void { 17 | assert.strictEqual(validation.fold(t.identity, t.identity), value); 18 | } 19 | 20 | export function assertDeepEqual(validation: t.Validation, value: T): void { 21 | assert.deepEqual(validation.fold(t.identity, t.identity), value); 22 | } 23 | 24 | export const string2 = new t.Type( 25 | 'string2', 26 | v => typeof v === 'string' && v[1] === '-', 27 | (s, c) => 28 | t.String.validate(s, c).chain(s => { 29 | if (s.length === 2) { 30 | return t.success(s[0] + '-' + s[1]); 31 | } else { 32 | return t.failure(s, c); 33 | } 34 | }), 35 | a => a[0] + a[2] 36 | ); 37 | 38 | export const DateFromNumber = new t.Type( 39 | 'DateFromNumber', 40 | v => v instanceof Date, 41 | (s, c) => 42 | t.Number.validate(s, c).chain(n => { 43 | const d = new Date(n); 44 | return isNaN(d.getTime()) ? t.failure(n, c) : t.success(d); 45 | }), 46 | a => a.getTime() 47 | ); 48 | 49 | export const NumberFromString = new t.Type( 50 | 'NumberFromString', 51 | t.Number.is, 52 | (s, c) => { 53 | const n = parseFloat(s); 54 | return isNaN(n) ? t.failure(s, c) : t.success(n); 55 | }, 56 | String 57 | ); 58 | 59 | export const IntegerFromString = t.refinement(NumberFromString, t.Integer.is, 'IntegerFromString'); 60 | 61 | export function withDefault( 62 | type: T, 63 | defaultValue: t.TypeOf 64 | ): t.Type, t.OutputOf, t.InputOf> { 65 | return new t.Type( 66 | `withDefault(${type.name}, ${JSON.stringify(defaultValue)})`, 67 | type.is, 68 | (v, c) => type.validate(v != null ? v : defaultValue, c), 69 | type.encode 70 | ); 71 | } 72 | 73 | const props = { number: t.Number, string: t.String }; 74 | export const NumberOrString = t.unionMap(props, v => typeof v); 75 | 76 | const props2 = { string: t.String, number: DateFromNumber }; 77 | export const DateFromNumOrStr = t.unionMap(props2, v => typeof v); 78 | export const DateFromNumOrStr2 = t.union([t.Number, DateFromNumber]); 79 | -------------------------------------------------------------------------------- /src/types/intersection.js: -------------------------------------------------------------------------------- 1 | //@flow 2 | // 3 | // intersections 4 | // 5 | import { isLeft } from '../fp'; 6 | import { Type, success, failures, identity, useIdentity, AggregateError } from './index.js'; 7 | import type { MixedFlowType, TypeOf, OutputOf } from './index.js'; 8 | 9 | interface IntersectionFunc { 10 | ( 11 | types: [A, B, C, D, E], 12 | name?: string 13 | ): Type< 14 | $PropertyType & 15 | $PropertyType & 16 | $PropertyType & 17 | $PropertyType & 18 | $PropertyType, 19 | $PropertyType & 20 | $PropertyType & 21 | $PropertyType & 22 | $PropertyType & 23 | $PropertyType, 24 | mixed 25 | >; 26 | ( 27 | types: [A, B, C, D], 28 | name?: string 29 | ): Type< 30 | $PropertyType & $PropertyType & $PropertyType & $PropertyType, 31 | $PropertyType & $PropertyType & $PropertyType & $PropertyType, 32 | mixed 33 | >; 34 | ( 35 | types: [A, B, C], 36 | name?: string 37 | ): Type< 38 | $PropertyType & $PropertyType & $PropertyType, 39 | $PropertyType & $PropertyType & $PropertyType, 40 | mixed 41 | >; 42 | ( 43 | types: [A, B], 44 | name?: string 45 | ): Type<$PropertyType & $PropertyType, $PropertyType & $PropertyType, mixed>; 46 | } 47 | 48 | export const intersection: IntersectionFunc = (_intersection: any); 49 | 50 | function _intersection>( 51 | types: RTS, 52 | name: string = `(${types.map(type => type.name).join(' & ')})` 53 | ) { 54 | return new Type( 55 | name, 56 | m => types.every(type => type.is(m)), 57 | (m, c) => { 58 | const errors = new AggregateError(); 59 | let a = m; 60 | types.forEach(type => { 61 | const validation = type.validate(a, c); 62 | if (validation.isLeft()) { 63 | errors.push(...validation.value); 64 | } else { 65 | a = validation.value; 66 | } 67 | }); 68 | return errors.length ? failures(errors) : success((a: any)); 69 | }, 70 | useIdentity(types) 71 | ? identity 72 | : a => { 73 | let s = a; 74 | types.forEach(type => (s = type.encode(s))); 75 | return s; 76 | } 77 | ); 78 | } 79 | -------------------------------------------------------------------------------- /src/types/dictionary.js: -------------------------------------------------------------------------------- 1 | //@flow 2 | // 3 | // dictionaries 4 | // 5 | import { isLeft } from '../fp'; 6 | import { 7 | Type, 8 | Any, 9 | isDictionary, 10 | success, 11 | failures, 12 | identity, 13 | appendContext, 14 | Dictionary, 15 | AggregateError 16 | } from './index.js'; 17 | import type { MixedFlowType, AnyFlowType, TypeOf, OutputOf } from './index.js'; 18 | 19 | export type TypeOfDictionary = { [K: TypeOf]: TypeOf }; 20 | 21 | export type OutputOfDictionary = { [K: OutputOf]: OutputOf }; 22 | 23 | export const dictionary = ( 24 | domain: D, 25 | codomain: C, 26 | name: string = `{ [K: ${domain.name}]: ${codomain.name} }` 27 | ): Type< 28 | { [K: $PropertyType]: $PropertyType }, 29 | { [K: $PropertyType]: $PropertyType }, 30 | mixed 31 | > => { 32 | const isIndexSignatureRequired = codomain !== Any; 33 | return new Type( 34 | name, 35 | m => isDictionary(m) && Object.keys(m).every(k => domain.is(k) && codomain.is(m[k])), 36 | (m, c) => { 37 | const dictionaryValidation = Dictionary.validate(m, c); 38 | if (isLeft(dictionaryValidation)) { 39 | return dictionaryValidation; 40 | } 41 | const o = dictionaryValidation.value; 42 | const a: { [key: string]: any } = {}; 43 | const errors = new AggregateError(); 44 | const keys = Object.keys(o); 45 | const len = keys.length; 46 | let changed: boolean = false; 47 | for (let i = 0; i < len; i++) { 48 | let k = keys[i]; 49 | const ok = o[k]; 50 | const domainValidation = domain.validate(k, appendContext(c, k, domain)); 51 | const codomainValidation = codomain.validate(ok, appendContext(c, k, codomain)); 52 | if (isLeft(domainValidation)) { 53 | errors.push(...domainValidation.value); 54 | } else { 55 | const vk = domainValidation.value; 56 | changed = changed || vk !== k; 57 | k = vk; 58 | } 59 | if (isLeft(codomainValidation)) { 60 | errors.push(...codomainValidation.value); 61 | } else { 62 | const vok = codomainValidation.value; 63 | changed = changed || vok !== ok; 64 | a[k] = vok; 65 | } 66 | } 67 | return errors.length ? failures(errors) : success(changed ? a : o); 68 | }, 69 | domain.encode === identity && codomain.encode === identity 70 | ? identity 71 | : a => { 72 | const s: { [key: string]: any } = {}; 73 | const keys = Object.keys(a); 74 | const len = keys.length; 75 | for (let i = 0; i < len; i++) { 76 | const k = keys[i]; 77 | s[String(domain.encode(k))] = codomain.encode(a[k]); 78 | } 79 | return s; 80 | } 81 | ); 82 | }; 83 | -------------------------------------------------------------------------------- /test/types/core.js: -------------------------------------------------------------------------------- 1 | //@flow 2 | import * as assert from 'assert'; 3 | import * as t from '../../src/index'; 4 | import { assertSuccess, assertFailure } from '../helpers'; 5 | import Bluebird from 'bluebird'; 6 | 7 | const BAA = new t.Type( 8 | 'BAA', 9 | t.Number.is, 10 | (s, c) => { 11 | const n = parseFloat(s); 12 | return isNaN(n) ? t.failure(s, c) : t.success(n); 13 | }, 14 | n => String(n) 15 | ); 16 | const nonEmptyString = new t.Type('BAA', v => typeof v === 'string' && v.length > 0); 17 | 18 | const BAI = t.String.pipe( 19 | BAA, 20 | 'T' 21 | ); 22 | 23 | describe('Type', () => { 24 | describe('assert', () => { 25 | it('should throw and use the function name as error message', () => { 26 | const isErr = err => { 27 | assert.ok(err instanceof t.AggregateError); 28 | assert.deepEqual(err.messages(), ['Invalid value null supplied to : number']); 29 | return true; 30 | }; 31 | assert.throws(() => t.Number.assert(null), isErr); 32 | }); 33 | 34 | it('should return value whene there are no errors', () => { 35 | assert.deepEqual(t.Number.assert(1), 1); 36 | }); 37 | }); 38 | 39 | describe('getAssert', () => { 40 | it('should resolve correct values', () => { 41 | const str: mixed = '1'; 42 | return Promise.resolve(str) 43 | .then(BAI.getAssert()) 44 | .then(v => assert.strictEqual(v, 1)); 45 | }); 46 | 47 | it('should reject incorrect value', () => { 48 | const str: mixed = 1; 49 | return Bluebird.resolve(str) 50 | .then(BAI.getAssert()) 51 | .then(() => assert.ok(false, 'should not resolve')) 52 | .catch(err => { 53 | assert.ok(err instanceof t.AggregateError); 54 | assert.deepEqual(err.messages(), ['Invalid value 1 supplied to : T']); 55 | }); 56 | }); 57 | }); 58 | 59 | describe('pipe', () => { 60 | it('should assign a default name', () => { 61 | const AOI = t.String; 62 | const T = AOI.pipe(BAA); 63 | assert.strictEqual(T.name, 'pipe(string, BAA)'); 64 | }); 65 | 66 | it('should combine two types', () => { 67 | assertSuccess(BAI.decode('1')); 68 | assertFailure(BAI.decode(1), ['Invalid value 1 supplied to : T']); 69 | assertFailure(BAI.decode('a'), ['Invalid value "a" supplied to : T']); 70 | assert.strictEqual(BAI.encode(2), '2'); 71 | }); 72 | 73 | it('should use identity as decoder function', () => { 74 | assert.strictEqual(t.String.pipe(nonEmptyString).encode, t.identity); 75 | }); 76 | }); 77 | 78 | describe('asDecoder', () => { 79 | it('should return a decoder', () => { 80 | assertSuccess(t.String.asDecoder().decode('1')); 81 | }); 82 | }); 83 | 84 | describe('asEncoder', () => { 85 | it('should return an encoder', () => { 86 | assert.strictEqual(BAI.asEncoder().encode(2), '2'); 87 | }); 88 | }); 89 | }); 90 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "io-flow-types", 3 | "version": "0.2.1", 4 | "description": "Flow compatible runtime type system for IO validation", 5 | "files": [ 6 | "lib" 7 | ], 8 | "main": "lib/index.js", 9 | "scripts": { 10 | "lint": "eslint src/**/*.js test/**/*.js", 11 | "jest": "jest test/index.js", 12 | "prettier": "prettier --single-quote --print-width 120 --parser flow --list-different \"{src,test}/**/*.js\"", 13 | "fix-prettier": "prettier --single-quote --print-width 120 --parser flow --write \"{src,test,examples,exercises}/**/*.js\"", 14 | "test": "npm run prettier && npm run lint && npm run jest && npm run flow-regression", 15 | "clean": "rimraf lib/*", 16 | "build": "npm run clean && npm run strip-flow", 17 | "strip-flow": "./node_modules/.bin/babel src --out-dir lib --copy-files && flow-copy-source src lib", 18 | "prepublishOnly": "npm run build", 19 | "flow": "flow", 20 | "flow-regression": "babel-node scripts/test.js" 21 | }, 22 | "repository": { 23 | "type": "git", 24 | "url": "https://github.com/orlandoc01/io-flow-types.git" 25 | }, 26 | "author": "Orlando Castillo ", 27 | "license": "MIT", 28 | "bugs": { 29 | "url": "https://github.com/orlandoc01/io-flow-types/issues" 30 | }, 31 | "homepage": "https://github.com/orlandoc01/io-flow-types", 32 | "peerDependencies": { 33 | "flow-bin": ">=0.81" 34 | }, 35 | "dependencies": {}, 36 | "devDependencies": { 37 | "@babel/cli": "^7.1.2", 38 | "@babel/core": "^7.1.2", 39 | "@babel/node": "^7.2.2", 40 | "@babel/plugin-proposal-class-properties": "^7.2.3", 41 | "@babel/preset-env": "^7.1.0", 42 | "@babel/preset-flow": "^7.0.0", 43 | "@babel/register": "^7.0.0", 44 | "@octokit/rest": "^16.7.0", 45 | "@types/benchmark": "1.0.31", 46 | "@types/jest": "22.2.2", 47 | "@types/node": "7.0.4", 48 | "babel-core": "^7.0.0-bridge.0", 49 | "babel-eslint": "^10.0.1", 50 | "babel-jest": "^23.6.0", 51 | "benchmark": "2.1.4", 52 | "bluebird": "^3.5.4", 53 | "eslint": "^5.8.0", 54 | "eslint-plugin-flowtype": "^3.1.1", 55 | "flow-bin": "^0.98.0", 56 | "flow-copy-source": "^2.0.2", 57 | "flow-typed": "^2.5.1", 58 | "got": "^9.5.0", 59 | "jest": "22.4.3", 60 | "prettier": "1.13.4", 61 | "regenerator-runtime": "^0.13.1", 62 | "rimraf": "2.6.2", 63 | "semver": "^5.6.0", 64 | "unzipper": "^0.9.7" 65 | }, 66 | "tags": [ 67 | "flow", 68 | "validation", 69 | "inference", 70 | "types", 71 | "runtime" 72 | ], 73 | "keywords": [ 74 | "typflow", 75 | "validation", 76 | "inference", 77 | "types", 78 | "runtime" 79 | ], 80 | "jest": { 81 | "globals": {}, 82 | "collectCoverage": true, 83 | "testRegex": "test", 84 | "moduleFileExtensions": [ 85 | "js" 86 | ], 87 | "testPathIgnorePatterns": [ 88 | "helpers.js" 89 | ], 90 | "coverageThreshold": { 91 | "global": { 92 | "branches": 100, 93 | "functions": 100, 94 | "lines": 100, 95 | "statements": 0 96 | } 97 | } 98 | } 99 | } 100 | -------------------------------------------------------------------------------- /test/types/intersection.js: -------------------------------------------------------------------------------- 1 | //@flow 2 | import * as assert from 'assert'; 3 | import * as t from '../../src/index'; 4 | import { assertSuccess, assertFailure, assertStrictEqual, assertDeepEqual, DateFromNumber } from '../helpers'; 5 | 6 | describe('intersection', () => { 7 | it('should properly typecheck values', () => { 8 | const p1 = t.inexactAll({ foo: DateFromNumber }); 9 | const p2 = t.inexactAll({ bar: t.String }); 10 | const T = t.intersection([p1, p2]); 11 | const a: t.TypeOf = { foo: new Date(), bar: 'hi' }; 12 | const b: t.OutputOf = { foo: 1, bar: 'hi' }; 13 | // $FlowExpectError 14 | const c: t.TypeOf = { foo: new Date() }; 15 | // $FlowExpectError 16 | const d: t.TypeOf = { bar: 'hi' }; 17 | // $FlowExpectError 18 | const e: t.OutputOf = { foo: 1, bar: 1 }; 19 | // $FlowExpectError 20 | const f: t.OutputOf = {}; 21 | }); 22 | 23 | it('should succeed validating a valid value', () => { 24 | const T = t.intersection([t.inexactAll({ a: t.Number }), t.inexactAll({ b: t.Number })]); 25 | assertSuccess(T.decode({ a: 1, b: 2 })); 26 | }); 27 | 28 | it('should keep unknown properties', () => { 29 | const T = t.intersection([t.inexactAll({ a: t.Number }), t.inexactAll({ b: t.Number })]); 30 | const validation = T.decode({ a: 1, b: 1, c: true }); 31 | if (validation.isRight()) { 32 | assert.deepEqual(validation.value, { a: 1, b: 1, c: true }); 33 | } else { 34 | assert.ok(false); 35 | } 36 | }); 37 | 38 | it('should return the same reference if validation succeeded and nothing changed', () => { 39 | const T = t.intersection([t.inexactAll({ a: t.Number }), t.inexactAll({ b: t.Number })]); 40 | const value = { a: 1, b: 2 }; 41 | assertStrictEqual(T.decode(value), value); 42 | }); 43 | 44 | it('should return a new reference if validation succeeded and something changed', () => { 45 | const T = t.intersection([t.inexactAll({ a: DateFromNumber }), t.inexactAll({ b: t.Number })]); 46 | assertDeepEqual(T.decode({ a: 1, b: 2 }), { a: new Date(1), b: 2 }); 47 | }); 48 | 49 | it('should fail validating an invalid value', () => { 50 | const T = t.intersection([t.inexactAll({ a: t.Number }), t.inexactAll({ b: t.Number })]); 51 | assertFailure(T.decode({ a: 1 }), [ 52 | 'Invalid value undefined supplied to : ({ a: number } & { b: number })/b: number' 53 | ]); 54 | }); 55 | 56 | it('should serialize a deserialized', () => { 57 | const T = t.intersection([t.inexactAll({ a: DateFromNumber }), t.inexactAll({ b: t.Number })]); 58 | assert.deepEqual(T.encode({ a: new Date(0), b: 1 }), { a: 0, b: 1 }); 59 | }); 60 | 61 | it('should return the same reference when serializing', () => { 62 | const T = t.intersection([t.inexactAll({ a: t.Number }), t.inexactAll({ b: t.Number })]); 63 | assert.strictEqual(T.encode, t.identity); 64 | }); 65 | 66 | it('should type guard', () => { 67 | const T = t.intersection([t.inexactAll({ a: DateFromNumber }), t.inexactAll({ b: t.Number })]); 68 | assert.strictEqual(T.is({ a: new Date(0), b: 1 }), true); 69 | assert.strictEqual(T.is({ a: new Date(0) }), false); 70 | assert.strictEqual(T.is(undefined), false); 71 | }); 72 | }); 73 | -------------------------------------------------------------------------------- /src/fp/index.js: -------------------------------------------------------------------------------- 1 | //@flow 2 | 3 | export type Either = Left | Right; 4 | 5 | class Left { 6 | +tag: 'Left'; 7 | +value: L; 8 | constructor(v: L) { 9 | this.value = v; 10 | this.tag = 'Left'; 11 | } 12 | map(__: (a: A) => B): Either { 13 | return (this: any); 14 | } 15 | mapLeft(f: (l: L) => M): Either { 16 | return new Left(f(this.value)); 17 | } 18 | getOrElse(a: A): A { 19 | return a; 20 | } 21 | inspect(): string { 22 | return this.toString(); 23 | } 24 | toString(): string { 25 | return `left(${toString(this.value)})`; 26 | } 27 | fold(whenLeft: (l: L) => B, whenRight: (a: A) => B): B { 28 | return whenLeft(this.value); 29 | } 30 | ap(fab: Either B>): Either { 31 | return ((isLeft(fab) ? fab : this): any); 32 | } 33 | chain(f: (a: A) => Either): Either { 34 | return (this: any); 35 | } 36 | isRight() { 37 | return false; 38 | } 39 | isLeft() { 40 | return true; 41 | } 42 | } 43 | 44 | class Right { 45 | +tag: 'Right'; 46 | +value: A; 47 | constructor(v: A) { 48 | this.value = v; 49 | this.tag = 'Right'; 50 | } 51 | map(f: (a: A) => B): Either { 52 | return new Right(f(this.value)); 53 | } 54 | mapLeft(_: (l: L) => M): Either { 55 | return new Right(this.value); 56 | } 57 | getOrElse(_: A): A { 58 | return this.value; 59 | } 60 | inspect(): string { 61 | return this.toString(); 62 | } 63 | toString(): string { 64 | return `right(${toString(this.value)})`; 65 | } 66 | fold(whenLeft: (l: L) => B, whenRight: (a: A) => B): B { 67 | return whenRight(this.value); 68 | } 69 | ap(fab: Either B>): Either { 70 | return isRight(fab) ? this.map(fab.value) : makeLeft(fab.value); 71 | } 72 | chain(f: (a: A) => Either): Either { 73 | return f(this.value); 74 | } 75 | isRight() { 76 | return true; 77 | } 78 | isLeft() { 79 | return false; 80 | } 81 | } 82 | 83 | export function isLeft(v: mixed): boolean %checks { 84 | return v instanceof Left; 85 | } 86 | export function isRight(v: mixed): boolean %checks { 87 | return v instanceof Right; 88 | } 89 | const makeLeft = (v: L): Either => new Left(v); 90 | const makeRight = (v: A): Either => new Right(v); 91 | 92 | export { makeLeft as Left, makeRight as Right }; 93 | 94 | export const getFunctionName = (f: Function | { displayName: string } | { name: string }): string => 95 | ``; 96 | 97 | export function toString(x: mixed): string { 98 | if (typeof x === 'string') { 99 | return JSON.stringify(x); 100 | } 101 | if (x instanceof Date) { 102 | return `new Date('${x.toISOString()}')`; 103 | } 104 | if (Array.isArray(x)) { 105 | return `[${x.map(toString).join(', ')}]`; 106 | } 107 | if (x == null) { 108 | return String(x); 109 | } 110 | if (typeof x === 'function') { 111 | return getFunctionName(x); 112 | } 113 | if (typeof x.toString === 'function' && x.toString !== Object.prototype.toString) { 114 | return (x.toString: any)(); 115 | } 116 | //Based on above checks, this should always return a string 117 | const result: string = (JSON.stringify(x, null): any); 118 | return result; 119 | } 120 | -------------------------------------------------------------------------------- /test/types/tuple.js: -------------------------------------------------------------------------------- 1 | //@flow 2 | import * as assert from 'assert'; 3 | import * as t from '../../src/index'; 4 | import { assertSuccess, assertFailure, assertStrictEqual, assertDeepEqual, DateFromNumber } from '../helpers'; 5 | 6 | describe('tuple', () => { 7 | it('should properly typecheck values', () => { 8 | const T = t.tuple([t.String, DateFromNumber]); 9 | const arr1: t.TypeOf = ['foo', new Date()]; 10 | const arr2: t.OutputOf = ['foo', 1]; 11 | // $FlowExpectError 12 | const arr3: t.TypeOf = 2; 13 | // $FlowExpectError 14 | const arr4: t.TypeOf = ['foo', 'bar']; 15 | // $FlowExpectError 16 | const arr5: t.OutputOf = [1, 2]; 17 | }); 18 | 19 | it('should succeed validating a valid value', () => { 20 | const T = t.tuple([t.Number, t.String]); 21 | assertSuccess(T.decode([1, 'a'])); 22 | }); 23 | 24 | it('should return the same reference if validation succeeded and nothing changed', () => { 25 | const T = t.tuple([t.Number, t.String]); 26 | const value = [1, 'a']; 27 | assertStrictEqual(T.decode(value), value); 28 | }); 29 | 30 | it('should return the a new reference if validation succeeded and something changed', () => { 31 | const T = t.tuple([DateFromNumber, t.String]); 32 | assertDeepEqual(T.decode([1, 'a']), [new Date(1), 'a']); 33 | }); 34 | 35 | it('should fail validating an invalid value', () => { 36 | const T = t.tuple([t.Number, t.String]); 37 | assertFailure(T.decode(1), ['Invalid value 1 supplied to : [number, string]']); 38 | assertFailure(T.decode([]), [ 39 | 'Invalid value undefined supplied to : [number, string]/0: number', 40 | 'Invalid value undefined supplied to : [number, string]/1: string' 41 | ]); 42 | assertFailure(T.decode([1]), ['Invalid value undefined supplied to : [number, string]/1: string']); 43 | assertFailure(T.decode([1, 1]), ['Invalid value 1 supplied to : [number, string]/1: string']); 44 | assertFailure(T.decode([1, 'foo', true]), ['Invalid value true supplied to : [number, string]/2: never']); 45 | }); 46 | 47 | it('should serialize a deserialized', () => { 48 | const T = t.tuple([DateFromNumber, t.String]); 49 | assert.deepEqual(T.encode([new Date(0), 'foo']), [0, 'foo']); 50 | }); 51 | 52 | it('should return the same reference when serializing', () => { 53 | const T = t.tuple([t.Number, t.String]); 54 | assert.strictEqual(T.encode, t.identity); 55 | }); 56 | 57 | it('should type guard', () => { 58 | const T1 = t.tuple([t.Number, t.String]); 59 | assert.strictEqual(T1.is([0, 'foo']), true); 60 | assert.strictEqual(T1.is([0, 2]), false); 61 | assert.strictEqual(T1.is(undefined), false); 62 | assert.strictEqual(T1.is([0]), false); 63 | assert.strictEqual(T1.is([0, 'foo', true]), false); 64 | const T2 = t.tuple([DateFromNumber, t.String]); 65 | assert.strictEqual(T2.is([new Date(0), 'foo']), true); 66 | assert.strictEqual(T2.is([new Date(0), 2]), false); 67 | assert.strictEqual(T2.is(undefined), false); 68 | assert.strictEqual(T2.is([new Date(0)]), false); 69 | assert.strictEqual(T2.is([new Date(0), 'foo', true]), false); 70 | }); 71 | 72 | it('should assign a default name', () => { 73 | const T1 = t.tuple([t.Number, t.String]); 74 | assert.strictEqual(T1.name, '[number, string]'); 75 | const T2 = t.tuple([t.Number, t.String], 'T2'); 76 | assert.strictEqual(T2.name, 'T2'); 77 | }); 78 | }); 79 | -------------------------------------------------------------------------------- /src/types/tuple.js: -------------------------------------------------------------------------------- 1 | //@flow 2 | // 3 | // tuples 4 | // 5 | import { isLeft } from '../fp'; 6 | import { 7 | Type, 8 | Never, 9 | ValidationError, 10 | useIdentity, 11 | success, 12 | failures, 13 | identity, 14 | appendContext, 15 | arrayType, 16 | AggregateError 17 | } from './index.js'; 18 | import type { MixedFlowType } from './index.js'; 19 | 20 | export interface TupleFunc { 21 | ( 22 | types: [A, B, C, D, E], 23 | name?: string 24 | ): Type< 25 | [ 26 | $PropertyType, 27 | $PropertyType, 28 | $PropertyType, 29 | $PropertyType, 30 | $PropertyType 31 | ], 32 | [ 33 | $PropertyType, 34 | $PropertyType, 35 | $PropertyType, 36 | $PropertyType, 37 | $PropertyType 38 | ], 39 | mixed 40 | >; 41 | ( 42 | types: [A, B, C, D], 43 | name?: string 44 | ): Type< 45 | [$PropertyType, $PropertyType, $PropertyType, $PropertyType], 46 | [$PropertyType, $PropertyType, $PropertyType, $PropertyType], 47 | mixed 48 | >; 49 | ( 50 | types: [A, B, C], 51 | name?: string 52 | ): Type< 53 | [$PropertyType, $PropertyType, $PropertyType], 54 | [$PropertyType, $PropertyType, $PropertyType], 55 | mixed 56 | >; 57 | ( 58 | types: [A, B], 59 | name?: string 60 | ): Type<[$PropertyType, $PropertyType], [$PropertyType, $PropertyType], mixed>; 61 | } 62 | function _tuple>(types: RTS, name: string = `[${types.map(type => type.name).join(', ')}]`) { 63 | const len = types.length; 64 | return new Type( 65 | name, 66 | m => Array.isArray(m) && m.length === len && types.every((type, i) => type.is(m[i])), 67 | (m, c) => { 68 | const arrayValidation = arrayType.validate(m, c); 69 | if (isLeft(arrayValidation)) { 70 | return arrayValidation; 71 | } else { 72 | const as = arrayValidation.value; 73 | let t: Array = as; 74 | const errors = new AggregateError(); 75 | for (let i = 0; i < len; i++) { 76 | const a = as[i]; 77 | const type = types[i]; 78 | const validation = type.validate(a, appendContext(c, String(i), type)); 79 | if (isLeft(validation)) { 80 | errors.push(...validation.value); 81 | } else { 82 | const va = validation.value; 83 | if (va !== a) { 84 | /* istanbul ignore next */ 85 | if (t === as) { 86 | t = as.slice(); 87 | } 88 | t[i] = va; 89 | } 90 | } 91 | } 92 | if (as.length > len) { 93 | errors.push(new ValidationError(as[len], appendContext(c, String(len), Never))); 94 | } 95 | return errors.length ? failures(errors) : success(t); 96 | } 97 | }, 98 | useIdentity(types) ? identity : a => types.map((type, i) => type.encode(a[i])) 99 | ); 100 | } 101 | 102 | export const tuple: TupleFunc = (_tuple: any); 103 | -------------------------------------------------------------------------------- /test/fp/index.js: -------------------------------------------------------------------------------- 1 | //@flow 2 | import * as assert from 'assert'; 3 | import { isLeft, isRight, Left as left, Right as right } from '../../src/index.js'; 4 | 5 | describe('Either', () => { 6 | it('fold', () => { 7 | const f = (s: string) => `left${s.length}`; 8 | const g = (s: string) => `right${s.length}`; 9 | assert.strictEqual(left('abc').fold(f, g), 'left3'); 10 | assert.strictEqual(right('abc').fold(f, g), 'right3'); 11 | }); 12 | 13 | it('map', () => { 14 | const f = (s: string): number => s.length; 15 | assert.deepEqual(right('abc').map(f), right(3)); 16 | assert.deepEqual(left('s').map(f), left('s')); 17 | assert.deepEqual(right('abc').map(f), right(3)); 18 | assert.deepEqual(left('s').map(f), left('s')); 19 | }); 20 | 21 | it('mapLeft', () => { 22 | const f = (s: string): number => s.length; 23 | assert.deepEqual(left('abc').mapLeft(f), left(3)); 24 | assert.deepEqual(right('s').mapLeft(f), right('s')); 25 | assert.deepEqual(left('abc').mapLeft(f), left(3)); 26 | assert.deepEqual(right('s').mapLeft(f), right('s')); 27 | }); 28 | 29 | it('ap', () => { 30 | const f = (s: string): number => s.length; 31 | assert.deepEqual(right('abc').ap(right number>(f)), right(3)); 32 | assert.deepEqual(left('a').ap(right number>(f)), left('a')); 33 | assert.deepEqual( 34 | right('abc').ap(left number>('a')), 35 | left('a') 36 | ); 37 | assert.deepEqual(left('b').ap(left number>('a')), left('a')); 38 | }); 39 | 40 | it('chain', () => { 41 | const f = (s: string) => right(s.length); 42 | assert.deepEqual(right('abc').chain(f), right(3)); 43 | assert.deepEqual(left('a').chain(f), left('a')); 44 | assert.deepEqual(right('abc').chain(f), right(3)); 45 | }); 46 | 47 | it('getOrElse', () => { 48 | assert.equal(right(12).getOrElse(17), 12); 49 | assert.equal(left(12).getOrElse(17), 17); 50 | }); 51 | 52 | it('isLeft', () => { 53 | assert.strictEqual(right(1).isLeft(), false); 54 | assert.strictEqual(left(1).isLeft(), true); 55 | assert.strictEqual(isLeft(right(1)), false); 56 | assert.strictEqual(isLeft(left(1)), true); 57 | }); 58 | 59 | it('isRight', () => { 60 | assert.strictEqual(right(1).isRight(), true); 61 | assert.strictEqual(left(1).isRight(), false); 62 | assert.strictEqual(isRight(right(1)), true); 63 | assert.strictEqual(isRight(left(1)), false); 64 | }); 65 | 66 | it('toString', () => { 67 | assert.strictEqual(right('bar').toString(), 'right("bar")'); 68 | assert.strictEqual(right('bar').inspect(), 'right("bar")'); 69 | assert.strictEqual(left('bar').toString(), 'left("bar")'); 70 | assert.strictEqual(left('bar').inspect(), 'left("bar")'); 71 | assert.strictEqual(left(true).inspect(), 'left(true)'); 72 | assert.strictEqual(left(1).inspect(), 'left(1)'); 73 | assert.strictEqual(left({ a: 1 }).inspect(), 'left({"a":1})'); 74 | assert.strictEqual(left(new Date(0)).inspect(), "left(new Date('1970-01-01T00:00:00.000Z'))"); 75 | assert.strictEqual(left([1, 2, 3]).inspect(), 'left([1, 2, 3])'); 76 | assert.strictEqual(left(null).inspect(), 'left(null)'); 77 | assert.strictEqual(left(() => {}).inspect(), 'left()'); 78 | }); 79 | }); 80 | -------------------------------------------------------------------------------- /src/types/props/exact.js: -------------------------------------------------------------------------------- 1 | //@flow 2 | import type { Props, Obj } from './base.js'; 3 | import { PropsType, empty, getNameFromProps } from './base.js'; 4 | import { InexactPropsType } from './inexact.js'; 5 | import type { MixedFlowType, Is, Validate, Context, Encode } from '../index.js'; 6 | 7 | type ExactObj = $Rest< 8 | {| ...$Exact, ...$Exact |}, 9 | $Rest 10 | >; 11 | 12 | export class ExactPropsType extends PropsType { 13 | constructor( 14 | /** a unique name for this runtime type */ 15 | name: string, 16 | /** The following two properties are used to describe the overall shape of the object*/ 17 | props: P, 18 | required: $ReadOnlyArray<$Keys

>, 19 | /** a custom type guard */ 20 | is?: Is, 21 | /** succeeds if a value of type I can be decoded to a value of type A */ 22 | validate?: Validate, 23 | /** converts a value of type A to a value of type O */ 24 | encode?: Encode 25 | ) { 26 | super(name, props, required, false, is, validate, encode); 27 | } 28 | readOnly(name?: string): ExactPropsType, O, I> { 29 | return lift(super.readOnly(name)); 30 | } 31 | shape(name?: string): ExactPropsType, $Rest, I> { 32 | return lift(super.shape(name)); 33 | } 34 | inexact(name?: string): InexactPropsType { 35 | const newName = name || getNameFromProps(this.props, this.required, false); 36 | return new InexactPropsType(newName, this.props, this.required); 37 | } 38 | } 39 | 40 | export function exact( 41 | props: { required?: Required, optional?: Optional }, 42 | name?: string 43 | ): ExactPropsType< 44 | {| ...$Exact, ...$Exact |}, 45 | $ObjMap, (v: V) => $PropertyType>, 46 | $ObjMap, (v: V) => $PropertyType>, 47 | mixed 48 | > { 49 | const required: Required = props.required || empty(); 50 | const optional: Optional = props.optional || empty(); 51 | const typeName = name || getNameFromProps({ ...required, ...optional }, Object.keys(required), true); 52 | const requiredKeys = Object.keys(required); 53 | const allTypes = { ...required, ...optional }; 54 | return new ExactPropsType(typeName, allTypes, requiredKeys); 55 | } 56 | 57 | export function exactAll( 58 | required: Required, 59 | name: string = getNameFromProps(required, Object.keys(required), true) 60 | ): ExactPropsType< 61 | {| ...$Exact |}, 62 | $ObjMap, (v: V) => $PropertyType>, 63 | $ObjMap, (v: V) => $PropertyType>, 64 | mixed 65 | > { 66 | return exact({ required }, name); 67 | } 68 | 69 | export function exactShape( 70 | optional: Optional, 71 | name: string = getNameFromProps(optional, [], true) 72 | ): ExactPropsType< 73 | {| ...$Exact |}, 74 | $ObjMap, (v: V) => $PropertyType>, 75 | $ObjMap, (v: V) => $PropertyType>, 76 | mixed 77 | > { 78 | return exact<{||}, Optional>({ optional }, name); 79 | } 80 | 81 | function lift(val: PropsType): ExactPropsType { 82 | const { props, required, name, is, validate, encode } = val; 83 | return new ExactPropsType(name, props, required, is, validate, encode); 84 | } 85 | -------------------------------------------------------------------------------- /flow-typed/npm/unzipper_vx.x.x.js: -------------------------------------------------------------------------------- 1 | // flow-typed signature: 30b7bea46e640dce7a79f46c6660f277 2 | // flow-typed version: <>/unzipper_v0.9.7/flow_v0.86.0 3 | 4 | /** 5 | * This is an autogenerated libdef stub for: 6 | * 7 | * 'unzipper' 8 | * 9 | * Fill this stub out by replacing all the `any` types. 10 | * 11 | * Once filled out, we encourage you to share your work with the 12 | * community by sending a pull request to: 13 | * https://github.com/flowtype/flow-typed 14 | */ 15 | 16 | declare module 'unzipper' { 17 | declare module.exports: any; 18 | } 19 | 20 | /** 21 | * We include stubs for each file inside this npm package in case you need to 22 | * require those files directly. Feel free to delete any files that aren't 23 | * needed. 24 | */ 25 | declare module 'unzipper/lib/Buffer' { 26 | declare module.exports: any; 27 | } 28 | 29 | declare module 'unzipper/lib/BufferStream' { 30 | declare module.exports: any; 31 | } 32 | 33 | declare module 'unzipper/lib/Decrypt' { 34 | declare module.exports: any; 35 | } 36 | 37 | declare module 'unzipper/lib/extract' { 38 | declare module.exports: any; 39 | } 40 | 41 | declare module 'unzipper/lib/NoopStream' { 42 | declare module.exports: any; 43 | } 44 | 45 | declare module 'unzipper/lib/Open/directory' { 46 | declare module.exports: any; 47 | } 48 | 49 | declare module 'unzipper/lib/Open/index' { 50 | declare module.exports: any; 51 | } 52 | 53 | declare module 'unzipper/lib/Open/unzip' { 54 | declare module.exports: any; 55 | } 56 | 57 | declare module 'unzipper/lib/parse' { 58 | declare module.exports: any; 59 | } 60 | 61 | declare module 'unzipper/lib/parseExtraField' { 62 | declare module.exports: any; 63 | } 64 | 65 | declare module 'unzipper/lib/parseOne' { 66 | declare module.exports: any; 67 | } 68 | 69 | declare module 'unzipper/lib/PullStream' { 70 | declare module.exports: any; 71 | } 72 | 73 | declare module 'unzipper/unzip' { 74 | declare module.exports: any; 75 | } 76 | 77 | // Filename aliases 78 | declare module 'unzipper/lib/Buffer.js' { 79 | declare module.exports: $Exports<'unzipper/lib/Buffer'>; 80 | } 81 | declare module 'unzipper/lib/BufferStream.js' { 82 | declare module.exports: $Exports<'unzipper/lib/BufferStream'>; 83 | } 84 | declare module 'unzipper/lib/Decrypt.js' { 85 | declare module.exports: $Exports<'unzipper/lib/Decrypt'>; 86 | } 87 | declare module 'unzipper/lib/extract.js' { 88 | declare module.exports: $Exports<'unzipper/lib/extract'>; 89 | } 90 | declare module 'unzipper/lib/NoopStream.js' { 91 | declare module.exports: $Exports<'unzipper/lib/NoopStream'>; 92 | } 93 | declare module 'unzipper/lib/Open/directory.js' { 94 | declare module.exports: $Exports<'unzipper/lib/Open/directory'>; 95 | } 96 | declare module 'unzipper/lib/Open/index.js' { 97 | declare module.exports: $Exports<'unzipper/lib/Open/index'>; 98 | } 99 | declare module 'unzipper/lib/Open/unzip.js' { 100 | declare module.exports: $Exports<'unzipper/lib/Open/unzip'>; 101 | } 102 | declare module 'unzipper/lib/parse.js' { 103 | declare module.exports: $Exports<'unzipper/lib/parse'>; 104 | } 105 | declare module 'unzipper/lib/parseExtraField.js' { 106 | declare module.exports: $Exports<'unzipper/lib/parseExtraField'>; 107 | } 108 | declare module 'unzipper/lib/parseOne.js' { 109 | declare module.exports: $Exports<'unzipper/lib/parseOne'>; 110 | } 111 | declare module 'unzipper/lib/PullStream.js' { 112 | declare module.exports: $Exports<'unzipper/lib/PullStream'>; 113 | } 114 | declare module 'unzipper/unzip.js' { 115 | declare module.exports: $Exports<'unzipper/unzip'>; 116 | } 117 | -------------------------------------------------------------------------------- /src/types/props/inexact.js: -------------------------------------------------------------------------------- 1 | //@flow 2 | import type { Props, Obj } from './base.js'; 3 | import { PropsType, empty, getNameFromProps } from './base.js'; 4 | import { ExactPropsType } from './exact.js'; 5 | import type { MixedFlowType, Is, Validate, Context, Encode } from '../index.js'; 6 | 7 | type Inexact = { 8 | ...$Rest<{| ...$Exact, ...$Exact |}, $Rest> 9 | }; 10 | 11 | export class InexactPropsType extends PropsType { 12 | constructor( 13 | /** a unique name for this runtime type */ 14 | name: string, 15 | /** The following two properties are used to describe the overall shape of the object*/ 16 | props: P, 17 | required: $ReadOnlyArray<$Keys

>, 18 | /** a custom type guard */ 19 | is?: Is, 20 | /** succeeds if a value of type I can be decoded to a value of type A */ 21 | validate?: Validate, 22 | /** converts a value of type A to a value of type O */ 23 | encode?: Encode 24 | ) { 25 | super(name, props, required, true, is, validate, encode); 26 | } 27 | readOnly(name?: string): InexactPropsType, O, I> { 28 | return lift(super.readOnly(name)); 29 | } 30 | shape(name?: string): InexactPropsType, $Rest, I> { 31 | return lift(super.shape(name)); 32 | } 33 | exact(name?: string): ExactPropsType, $Exact, I> { 34 | const newName = name || getNameFromProps(this.props, this.required, true); 35 | return new ExactPropsType(newName, this.props, this.required); 36 | } 37 | } 38 | 39 | export function inexact( 40 | props: { required?: Required, optional?: Optional }, 41 | name?: string 42 | ): InexactPropsType< 43 | {| ...$Exact, ...$Exact |}, 44 | $ObjMap, (v: V) => $PropertyType>, 45 | $ObjMap, (v: V) => $PropertyType>, 46 | mixed 47 | > { 48 | const required: Required = props.required || empty(); 49 | const optional: Optional = props.optional || empty(); 50 | const typeName = name || getNameFromProps({ ...required, ...optional }, Object.keys(required), false); 51 | const requiredKeys = Object.keys(required); 52 | const allTypes = { ...required, ...optional }; 53 | return new InexactPropsType(typeName, allTypes, requiredKeys); 54 | } 55 | 56 | export function inexactAll( 57 | required: Required, 58 | name: string = getNameFromProps(required, Object.keys(required), false) 59 | ): InexactPropsType< 60 | {| ...$Exact |}, 61 | $ObjMap, (v: V) => $PropertyType>, 62 | $ObjMap, (v: V) => $PropertyType>, 63 | mixed 64 | > { 65 | return inexact({ required }, name); 66 | } 67 | 68 | export function inexactShape( 69 | optional: Optional, 70 | name: string = getNameFromProps(optional, [], false) 71 | ): InexactPropsType< 72 | {| ...$Exact |}, 73 | $ObjMap, (v: V) => $PropertyType>, 74 | $ObjMap, (v: V) => $PropertyType>, 75 | mixed 76 | > { 77 | return inexact<{||}, Optional>({ optional }, name); 78 | } 79 | 80 | function lift(val: PropsType): InexactPropsType { 81 | const { props, required, name, is, validate, encode } = val; 82 | return new InexactPropsType(name, props, required, is, validate, encode); 83 | } 84 | -------------------------------------------------------------------------------- /flow-typed/npm/got_vx.x.x.js: -------------------------------------------------------------------------------- 1 | // flow-typed signature: f4366efec78e8d77cf9e114af78e6734 2 | // flow-typed version: <>/got_v9.5/flow_v0.86.0 3 | 4 | /** 5 | * This is an autogenerated libdef stub for: 6 | * 7 | * 'got' 8 | * 9 | * Fill this stub out by replacing all the `any` types. 10 | * 11 | * Once filled out, we encourage you to share your work with the 12 | * community by sending a pull request to: 13 | * https://github.com/flowtype/flow-typed 14 | */ 15 | 16 | declare module 'got' { 17 | declare module.exports: any; 18 | } 19 | 20 | /** 21 | * We include stubs for each file inside this npm package in case you need to 22 | * require those files directly. Feel free to delete any files that aren't 23 | * needed. 24 | */ 25 | declare module 'got/source/as-promise' { 26 | declare module.exports: any; 27 | } 28 | 29 | declare module 'got/source/as-stream' { 30 | declare module.exports: any; 31 | } 32 | 33 | declare module 'got/source/create' { 34 | declare module.exports: any; 35 | } 36 | 37 | declare module 'got/source/errors' { 38 | declare module.exports: any; 39 | } 40 | 41 | declare module 'got/source/get-response' { 42 | declare module.exports: any; 43 | } 44 | 45 | declare module 'got/source/index' { 46 | declare module.exports: any; 47 | } 48 | 49 | declare module 'got/source/known-hook-events' { 50 | declare module.exports: any; 51 | } 52 | 53 | declare module 'got/source/merge' { 54 | declare module.exports: any; 55 | } 56 | 57 | declare module 'got/source/normalize-arguments' { 58 | declare module.exports: any; 59 | } 60 | 61 | declare module 'got/source/progress' { 62 | declare module.exports: any; 63 | } 64 | 65 | declare module 'got/source/request-as-event-emitter' { 66 | declare module.exports: any; 67 | } 68 | 69 | declare module 'got/source/utils/deep-freeze' { 70 | declare module.exports: any; 71 | } 72 | 73 | declare module 'got/source/utils/get-body-size' { 74 | declare module.exports: any; 75 | } 76 | 77 | declare module 'got/source/utils/is-form-data' { 78 | declare module.exports: any; 79 | } 80 | 81 | declare module 'got/source/utils/timed-out' { 82 | declare module.exports: any; 83 | } 84 | 85 | declare module 'got/source/utils/url-to-options' { 86 | declare module.exports: any; 87 | } 88 | 89 | // Filename aliases 90 | declare module 'got/source/as-promise.js' { 91 | declare module.exports: $Exports<'got/source/as-promise'>; 92 | } 93 | declare module 'got/source/as-stream.js' { 94 | declare module.exports: $Exports<'got/source/as-stream'>; 95 | } 96 | declare module 'got/source/create.js' { 97 | declare module.exports: $Exports<'got/source/create'>; 98 | } 99 | declare module 'got/source/errors.js' { 100 | declare module.exports: $Exports<'got/source/errors'>; 101 | } 102 | declare module 'got/source/get-response.js' { 103 | declare module.exports: $Exports<'got/source/get-response'>; 104 | } 105 | declare module 'got/source/index.js' { 106 | declare module.exports: $Exports<'got/source/index'>; 107 | } 108 | declare module 'got/source/known-hook-events.js' { 109 | declare module.exports: $Exports<'got/source/known-hook-events'>; 110 | } 111 | declare module 'got/source/merge.js' { 112 | declare module.exports: $Exports<'got/source/merge'>; 113 | } 114 | declare module 'got/source/normalize-arguments.js' { 115 | declare module.exports: $Exports<'got/source/normalize-arguments'>; 116 | } 117 | declare module 'got/source/progress.js' { 118 | declare module.exports: $Exports<'got/source/progress'>; 119 | } 120 | declare module 'got/source/request-as-event-emitter.js' { 121 | declare module.exports: $Exports<'got/source/request-as-event-emitter'>; 122 | } 123 | declare module 'got/source/utils/deep-freeze.js' { 124 | declare module.exports: $Exports<'got/source/utils/deep-freeze'>; 125 | } 126 | declare module 'got/source/utils/get-body-size.js' { 127 | declare module.exports: $Exports<'got/source/utils/get-body-size'>; 128 | } 129 | declare module 'got/source/utils/is-form-data.js' { 130 | declare module.exports: $Exports<'got/source/utils/is-form-data'>; 131 | } 132 | declare module 'got/source/utils/timed-out.js' { 133 | declare module.exports: $Exports<'got/source/utils/timed-out'>; 134 | } 135 | declare module 'got/source/utils/url-to-options.js' { 136 | declare module.exports: $Exports<'got/source/utils/url-to-options'>; 137 | } 138 | -------------------------------------------------------------------------------- /src/types/union.js: -------------------------------------------------------------------------------- 1 | //@flow 2 | // 3 | // unions 4 | // 5 | import { isLeft, isRight } from '../fp'; 6 | import { 7 | Type, 8 | failures, 9 | identity, 10 | appendContext, 11 | useIdentity, 12 | Number, 13 | String as stringType, 14 | Never, 15 | isObject, 16 | values, 17 | AggregateError 18 | } from './index.js'; 19 | import type { MixedFlowType, TypeOf, OutputOf } from './index.js'; 20 | import type { Props } from './props'; 21 | 22 | type MappedUnion = { [key: string]: MixedFlowType }; 23 | export function unionMap( 24 | map: P, 25 | toKey: (mixed => string) | string, 26 | name?: string = `(${values(map) 27 | .map(type => type.name) 28 | .join(' | ')})` 29 | ): Type< 30 | $Values<$ObjMap(v: V) => $PropertyType>>, 31 | $Values<$ObjMap(v: V) => $PropertyType>>, 32 | mixed 33 | > { 34 | const transform: mixed => string = typeof toKey === 'string' ? makeToKey(toKey) : toKey; 35 | const is = (v: mixed) => (map[transform(v)] || Never).is(v); 36 | return new Type(name, is, validate, makeEncode()); 37 | 38 | function validate(m, c) { 39 | const type = map[transform(m)] || Never; 40 | return type.validate(m, c); 41 | } 42 | function makeEncode() { 43 | const types = values(map); 44 | const findAndEncode = a => { 45 | const type = map[transform(a)] || Never; 46 | return type.encode(a); 47 | }; 48 | return useIdentity(types) ? identity : findAndEncode; 49 | } 50 | function makeToKey(key: string) { 51 | return (v: mixed): string => { 52 | const val = isObject(v) ? v[key] : ''; 53 | return String(val); 54 | }; 55 | } 56 | } 57 | 58 | export const union: UnionFunc = (_union: any); 59 | 60 | interface UnionFunc { 61 | ( 62 | types: [A, B, C, D, E], 63 | name?: string 64 | ): Type< 65 | | $PropertyType 66 | | $PropertyType 67 | | $PropertyType 68 | | $PropertyType 69 | | $PropertyType, 70 | | $PropertyType 71 | | $PropertyType 72 | | $PropertyType 73 | | $PropertyType 74 | | $PropertyType, 75 | mixed 76 | >; 77 | ( 78 | types: [A, B, C, D], 79 | name?: string 80 | ): Type< 81 | $PropertyType | $PropertyType | $PropertyType | $PropertyType, 82 | $PropertyType | $PropertyType | $PropertyType | $PropertyType, 83 | mixed 84 | >; 85 | ( 86 | types: [A, B, C], 87 | name?: string 88 | ): Type< 89 | $PropertyType | $PropertyType | $PropertyType, 90 | $PropertyType | $PropertyType | $PropertyType, 91 | mixed 92 | >; 93 | ( 94 | types: [A, B], 95 | name?: string 96 | ): Type<$PropertyType | $PropertyType, $PropertyType | $PropertyType, mixed>; 97 | } 98 | 99 | type MixedFlowTypeFlowTypeTypeArray = $ReadOnlyArray; 100 | export function _union( 101 | types: Arr, 102 | name: string = `(${types.map(type => type.name).join(' | ')})` 103 | ): Type<$PropertyType<$ElementType, '_A'>, $PropertyType<$ElementType, '_O'>, mixed> { 104 | const len = types.length; 105 | const is = m => types.some(type => type.is(m)); 106 | return new Type(name, is, validate, makeEncode()); 107 | 108 | function validate(m, c) { 109 | const errors = new AggregateError(); 110 | for (let i = 0; i < len; i++) { 111 | const type = types[i]; 112 | const validation = type.validate(m, appendContext(c, String(i), type)); 113 | if (isRight(validation)) return validation; 114 | else errors.push(...validation.value); 115 | } 116 | return failures(errors); 117 | } 118 | function makeEncode() { 119 | const findAndEncode = a => { 120 | const matchedType = types.find(type => type.is(a)) || Never; 121 | return matchedType.encode(a); 122 | }; 123 | return useIdentity(types) ? identity : findAndEncode; 124 | } 125 | } 126 | -------------------------------------------------------------------------------- /flow-typed/npm/@octokit/rest_vx.x.x.js: -------------------------------------------------------------------------------- 1 | // flow-typed signature: 04cccabcca5c18d1b79f76df9c06ed49 2 | // flow-typed version: <>/@octokit/rest_v16.7/flow_v0.86.0 3 | 4 | /** 5 | * This is an autogenerated libdef stub for: 6 | * 7 | * '@octokit/rest' 8 | * 9 | * Fill this stub out by replacing all the `any` types. 10 | * 11 | * Once filled out, we encourage you to share your work with the 12 | * community by sending a pull request to: 13 | * https://github.com/flowtype/flow-typed 14 | */ 15 | 16 | declare module '@octokit/rest' { 17 | declare module.exports: any; 18 | } 19 | 20 | /** 21 | * We include stubs for each file inside this npm package in case you need to 22 | * require those files directly. Feel free to delete any files that aren't 23 | * needed. 24 | */ 25 | declare module '@octokit/rest/lib/constructor' { 26 | declare module.exports: any; 27 | } 28 | 29 | declare module '@octokit/rest/lib/core' { 30 | declare module.exports: any; 31 | } 32 | 33 | declare module '@octokit/rest/lib/factory' { 34 | declare module.exports: any; 35 | } 36 | 37 | declare module '@octokit/rest/lib/parse-client-options' { 38 | declare module.exports: any; 39 | } 40 | 41 | declare module '@octokit/rest/lib/register-plugin' { 42 | declare module.exports: any; 43 | } 44 | 45 | declare module '@octokit/rest/lib/request-with-defaults' { 46 | declare module.exports: any; 47 | } 48 | 49 | declare module '@octokit/rest/plugins/authentication/authenticate' { 50 | declare module.exports: any; 51 | } 52 | 53 | declare module '@octokit/rest/plugins/authentication/before-request' { 54 | declare module.exports: any; 55 | } 56 | 57 | declare module '@octokit/rest/plugins/authentication/index' { 58 | declare module.exports: any; 59 | } 60 | 61 | declare module '@octokit/rest/plugins/pagination/index' { 62 | declare module.exports: any; 63 | } 64 | 65 | declare module '@octokit/rest/plugins/pagination/iterator' { 66 | declare module.exports: any; 67 | } 68 | 69 | declare module '@octokit/rest/plugins/pagination/paginate' { 70 | declare module.exports: any; 71 | } 72 | 73 | declare module '@octokit/rest/plugins/register-endpoints/index' { 74 | declare module.exports: any; 75 | } 76 | 77 | declare module '@octokit/rest/plugins/register-endpoints/register-endpoints' { 78 | declare module.exports: any; 79 | } 80 | 81 | declare module '@octokit/rest/plugins/rest-api-endpoints/index' { 82 | declare module.exports: any; 83 | } 84 | 85 | declare module '@octokit/rest/plugins/validate/index' { 86 | declare module.exports: any; 87 | } 88 | 89 | declare module '@octokit/rest/plugins/validate/validate' { 90 | declare module.exports: any; 91 | } 92 | 93 | // Filename aliases 94 | declare module '@octokit/rest/index' { 95 | declare module.exports: $Exports<'@octokit/rest'>; 96 | } 97 | declare module '@octokit/rest/index.js' { 98 | declare module.exports: $Exports<'@octokit/rest'>; 99 | } 100 | declare module '@octokit/rest/lib/constructor.js' { 101 | declare module.exports: $Exports<'@octokit/rest/lib/constructor'>; 102 | } 103 | declare module '@octokit/rest/lib/core.js' { 104 | declare module.exports: $Exports<'@octokit/rest/lib/core'>; 105 | } 106 | declare module '@octokit/rest/lib/factory.js' { 107 | declare module.exports: $Exports<'@octokit/rest/lib/factory'>; 108 | } 109 | declare module '@octokit/rest/lib/parse-client-options.js' { 110 | declare module.exports: $Exports<'@octokit/rest/lib/parse-client-options'>; 111 | } 112 | declare module '@octokit/rest/lib/register-plugin.js' { 113 | declare module.exports: $Exports<'@octokit/rest/lib/register-plugin'>; 114 | } 115 | declare module '@octokit/rest/lib/request-with-defaults.js' { 116 | declare module.exports: $Exports<'@octokit/rest/lib/request-with-defaults'>; 117 | } 118 | declare module '@octokit/rest/plugins/authentication/authenticate.js' { 119 | declare module.exports: $Exports<'@octokit/rest/plugins/authentication/authenticate'>; 120 | } 121 | declare module '@octokit/rest/plugins/authentication/before-request.js' { 122 | declare module.exports: $Exports<'@octokit/rest/plugins/authentication/before-request'>; 123 | } 124 | declare module '@octokit/rest/plugins/authentication/index.js' { 125 | declare module.exports: $Exports<'@octokit/rest/plugins/authentication/index'>; 126 | } 127 | declare module '@octokit/rest/plugins/pagination/index.js' { 128 | declare module.exports: $Exports<'@octokit/rest/plugins/pagination/index'>; 129 | } 130 | declare module '@octokit/rest/plugins/pagination/iterator.js' { 131 | declare module.exports: $Exports<'@octokit/rest/plugins/pagination/iterator'>; 132 | } 133 | declare module '@octokit/rest/plugins/pagination/paginate.js' { 134 | declare module.exports: $Exports<'@octokit/rest/plugins/pagination/paginate'>; 135 | } 136 | declare module '@octokit/rest/plugins/register-endpoints/index.js' { 137 | declare module.exports: $Exports<'@octokit/rest/plugins/register-endpoints/index'>; 138 | } 139 | declare module '@octokit/rest/plugins/register-endpoints/register-endpoints.js' { 140 | declare module.exports: $Exports<'@octokit/rest/plugins/register-endpoints/register-endpoints'>; 141 | } 142 | declare module '@octokit/rest/plugins/rest-api-endpoints/index.js' { 143 | declare module.exports: $Exports<'@octokit/rest/plugins/rest-api-endpoints/index'>; 144 | } 145 | declare module '@octokit/rest/plugins/validate/index.js' { 146 | declare module.exports: $Exports<'@octokit/rest/plugins/validate/index'>; 147 | } 148 | declare module '@octokit/rest/plugins/validate/validate.js' { 149 | declare module.exports: $Exports<'@octokit/rest/plugins/validate/validate'>; 150 | } 151 | -------------------------------------------------------------------------------- /src/types/props/base.js: -------------------------------------------------------------------------------- 1 | //@flow 2 | import { isLeft } from '../../fp'; 3 | import { 4 | mapValues, 5 | values, 6 | failure, 7 | Type, 8 | success, 9 | failures, 10 | identity, 11 | appendContext, 12 | Dictionary, 13 | Any, 14 | Never, 15 | AggregateError 16 | } from '../index.js'; 17 | import type { 18 | MixedFlowType, 19 | AnyFlowType, 20 | TypeOf, 21 | OutputOf, 22 | Validation, 23 | Is, 24 | Validate, 25 | Encode, 26 | Context 27 | } from '../index.js'; 28 | 29 | export type AnyProps = $ReadOnly<{ [key: string]: AnyFlowType }>; 30 | export type Props = $ReadOnly<{ [key: string]: MixedFlowType }>; 31 | 32 | export type Obj = $ReadOnly<{ [key: string]: mixed }>; 33 | 34 | export const empty = (): T => ({}: any); 35 | export const getNameFromProps = ( 36 | props: Props, 37 | required: $ReadOnlyArray, 38 | exact: boolean, 39 | readonly?: boolean = false 40 | ): string => { 41 | const requiredSet = new Set(required); 42 | const brackets: [string, string] = exact ? ['{|', '|}'] : ['{', '}']; 43 | return `${brackets[0]} ${Object.keys(props) 44 | .map(k => `${readonly ? '+' : ''}${k}${requiredSet.has(k) ? '' : '?'}: ${props[k].name}`) 45 | .join(', ')} ${brackets[1]}`; 46 | }; 47 | 48 | export class PropsType extends Type { 49 | +props: P; 50 | +required: $ReadOnlyArray<$Keys

>; 51 | +allowAdditional: boolean; 52 | constructor( 53 | /** a unique name for this runtime type */ 54 | name: string, 55 | /** The following three properties are used to describe the overall shape of the object*/ 56 | props: P, 57 | required: $ReadOnlyArray<$Keys

>, 58 | allowAdditional: boolean, 59 | /** a custom type guard */ 60 | is?: Is = makeIs(props, required, allowAdditional), 61 | /** succeeds if a value of type I can be decoded to a value of type A */ 62 | validate?: Validate = makeValidate(props, new Set(required), allowAdditional), 63 | /** converts a value of type A to a value of type O */ 64 | encode?: Encode = makeEncode(props) 65 | ) { 66 | super(name, is, validate, encode); 67 | this.props = props; 68 | this.required = required; 69 | this.allowAdditional = allowAdditional; 70 | } 71 | readOnly(name?: string): PropsType, O, I> { 72 | const newName = name || getNameFromProps(this.props, this.required, !this.allowAdditional, true); 73 | const isNotProduction = process.env.NODE_ENV !== 'production'; 74 | const validate = (m, c) => this.validate(m, c).map(x => (isNotProduction ? Object.freeze(x) : x)); 75 | return new PropsType(newName, this.props, this.required, this.allowAdditional, this.is, validate); 76 | } 77 | shape(name?: string): PropsType, $Rest, I> { 78 | const newName = name || getNameFromProps(this.props, [], !this.allowAdditional); 79 | return new PropsType(newName, this.props, [], this.allowAdditional); 80 | } 81 | } 82 | 83 | //Helper Functions 84 | function makeIs(props: P, required: $ReadOnlyArray<$Keys

>, allowAdditional: boolean) { 85 | return (m: mixed): boolean => { 86 | const v = m; 87 | if (v === null || typeof v !== 'object') { 88 | return false; 89 | } 90 | //First check to ensure all required keys are present in the object 91 | let hasAllRequired = required.every(requiredKey => requiredKey in v); 92 | //Next, check that all key/value pairs in the object are valid 93 | const isMatch = 94 | hasAllRequired && 95 | Object.keys(v).every(key => { 96 | const type = props[key]; 97 | return (type && type.is(v[key])) || (type == undefined && allowAdditional); 98 | }); 99 | return isMatch; 100 | }; 101 | } 102 | 103 | function makeValidate( 104 | props: P, 105 | required: Set<$Keys

>, 106 | allowAdditional: boolean 107 | ): Validate { 108 | const optional = Object.keys(props).filter(key => !(key in required)); 109 | return (m: I, c: Context): Validation => { 110 | const dictionaryValidation = Dictionary.validate(m, c); 111 | if (isLeft(dictionaryValidation)) return dictionaryValidation; 112 | const o = dictionaryValidation.value; 113 | const errors = new AggregateError(); 114 | const requiredKeysLeft = new Set(required); 115 | const fallbackType = allowAdditional ? Any : Never; 116 | const a: A = (Object.keys(o).reduce(validateValue, o): any); 117 | requiredKeysLeft.forEach(failUnusedRequiredKey); 118 | return errors.length ? failures(errors) : success(a); 119 | 120 | function validateValue(acc: Object, key: string) { 121 | const ok = o[key]; 122 | const type: MixedFlowType = props[key] || fallbackType; 123 | const validation = type.validate(ok, appendContext(c, key, type)); 124 | requiredKeysLeft.delete(key); 125 | if (isLeft(validation)) { 126 | errors.push(...validation.value); 127 | return acc; 128 | } 129 | const vok = validation.value; 130 | const updatedAcc = vok !== ok && acc === o ? { ...o } : acc; 131 | updatedAcc[key] = vok; 132 | return updatedAcc; 133 | } 134 | function failUnusedRequiredKey(key) { 135 | const type = props[key]; 136 | const err = failure(undefined, appendContext(c, key, type)); 137 | errors.push(...err.value); 138 | } 139 | }; 140 | } 141 | 142 | function makeEncode(props: P): Encode { 143 | const typeValues: Array = values(props); 144 | const encodeVal = (val, key) => (props[key] || Any).encode(val); 145 | const encodeAllVals = (a: A) => mapValues(a, encodeVal); 146 | const encode = typeValues.every(type => type.encode === identity) ? identity : encodeAllVals; 147 | return (encode: any); 148 | } 149 | -------------------------------------------------------------------------------- /flow-typed/npm/semver_v5.1.x.js: -------------------------------------------------------------------------------- 1 | // flow-typed signature: dc381ee55406f66b7272c6343db0834b 2 | // flow-typed version: da30fe6876/semver_v5.1.x/flow_>=v0.25.x 3 | 4 | declare module "semver" { 5 | declare type Release = 6 | | "major" 7 | | "premajor" 8 | | "minor" 9 | | "preminor" 10 | | "patch" 11 | | "prepatch" 12 | | "prerelease"; 13 | 14 | // The supported comparators are taken from the source here: 15 | // https://github.com/npm/node-semver/blob/8bd070b550db2646362c9883c8d008d32f66a234/semver.js#L623 16 | declare type Operator = 17 | | "===" 18 | | "!==" 19 | | "==" 20 | | "=" 21 | | "" // Not sure why you would want this, but whatever. 22 | | "!=" 23 | | ">" 24 | | ">=" 25 | | "<" 26 | | "<="; 27 | 28 | declare class SemVer { 29 | build: Array; 30 | loose: ?boolean; 31 | major: number; 32 | minor: number; 33 | patch: number; 34 | prerelease: Array; 35 | raw: string; 36 | version: string; 37 | 38 | constructor(version: string | SemVer, loose?: boolean): SemVer; 39 | compare(other: string | SemVer): -1 | 0 | 1; 40 | compareMain(other: string | SemVer): -1 | 0 | 1; 41 | comparePre(other: string | SemVer): -1 | 0 | 1; 42 | format(): string; 43 | inc(release: Release, identifier: string): this; 44 | } 45 | 46 | declare class Comparator { 47 | loose?: boolean; 48 | operator: Operator; 49 | semver: SemVer; 50 | value: string; 51 | 52 | constructor(comp: string | Comparator, loose?: boolean): Comparator; 53 | parse(comp: string): void; 54 | test(version: string): boolean; 55 | } 56 | 57 | declare class Range { 58 | loose: ?boolean; 59 | raw: string; 60 | set: Array>; 61 | 62 | constructor(range: string | Range, loose?: boolean): Range; 63 | format(): string; 64 | parseRange(range: string): Array; 65 | test(version: string): boolean; 66 | toString(): string; 67 | } 68 | 69 | declare var SEMVER_SPEC_VERSION: string; 70 | declare var re: Array; 71 | declare var src: Array; 72 | 73 | // Functions 74 | declare function valid(v: string | SemVer, loose?: boolean): string | null; 75 | declare function clean(v: string | SemVer, loose?: boolean): string | null; 76 | declare function inc( 77 | v: string | SemVer, 78 | release: Release, 79 | loose?: boolean, 80 | identifier?: string 81 | ): string | null; 82 | declare function inc( 83 | v: string | SemVer, 84 | release: Release, 85 | identifier: string 86 | ): string | null; 87 | declare function major(v: string | SemVer, loose?: boolean): number; 88 | declare function minor(v: string | SemVer, loose?: boolean): number; 89 | declare function patch(v: string | SemVer, loose?: boolean): number; 90 | 91 | // Comparison 92 | declare function gt( 93 | v1: string | SemVer, 94 | v2: string | SemVer, 95 | loose?: boolean 96 | ): boolean; 97 | declare function gte( 98 | v1: string | SemVer, 99 | v2: string | SemVer, 100 | loose?: boolean 101 | ): boolean; 102 | declare function lt( 103 | v1: string | SemVer, 104 | v2: string | SemVer, 105 | loose?: boolean 106 | ): boolean; 107 | declare function lte( 108 | v1: string | SemVer, 109 | v2: string | SemVer, 110 | loose?: boolean 111 | ): boolean; 112 | declare function eq( 113 | v1: string | SemVer, 114 | v2: string | SemVer, 115 | loose?: boolean 116 | ): boolean; 117 | declare function neq( 118 | v1: string | SemVer, 119 | v2: string | SemVer, 120 | loose?: boolean 121 | ): boolean; 122 | declare function cmp( 123 | v1: string | SemVer, 124 | comparator: Operator, 125 | v2: string | SemVer, 126 | loose?: boolean 127 | ): boolean; 128 | declare function compare( 129 | v1: string | SemVer, 130 | v2: string | SemVer, 131 | loose?: boolean 132 | ): -1 | 0 | 1; 133 | declare function rcompare( 134 | v1: string | SemVer, 135 | v2: string | SemVer, 136 | loose?: boolean 137 | ): -1 | 0 | 1; 138 | declare function compareLoose( 139 | v1: string | SemVer, 140 | v2: string | SemVer 141 | ): -1 | 0 | 1; 142 | declare function diff(v1: string | SemVer, v2: string | SemVer): ?Release; 143 | declare function sort( 144 | list: Array, 145 | loose?: boolean 146 | ): Array; 147 | declare function rsort( 148 | list: Array, 149 | loose?: boolean 150 | ): Array; 151 | declare function compareIdentifiers( 152 | v1: string | SemVer, 153 | v2: string | SemVer 154 | ): -1 | 0 | 1; 155 | declare function rcompareIdentifiers( 156 | v1: string | SemVer, 157 | v2: string | SemVer 158 | ): -1 | 0 | 1; 159 | 160 | // Ranges 161 | declare function validRange( 162 | range: string | Range, 163 | loose?: boolean 164 | ): string | null; 165 | declare function satisfies( 166 | version: string | SemVer, 167 | range: string | Range, 168 | loose?: boolean 169 | ): boolean; 170 | declare function maxSatisfying( 171 | versions: Array, 172 | range: string | Range, 173 | loose?: boolean 174 | ): string | SemVer | null; 175 | declare function gtr( 176 | version: string | SemVer, 177 | range: string | Range, 178 | loose?: boolean 179 | ): boolean; 180 | declare function ltr( 181 | version: string | SemVer, 182 | range: string | Range, 183 | loose?: boolean 184 | ): boolean; 185 | declare function outside( 186 | version: string | SemVer, 187 | range: string | Range, 188 | hilo: ">" | "<", 189 | loose?: boolean 190 | ): boolean; 191 | 192 | // Not explicitly documented, or deprecated 193 | declare function parse(version: string, loose?: boolean): ?SemVer; 194 | declare function toComparators( 195 | range: string | Range, 196 | loose?: boolean 197 | ): Array>; 198 | } 199 | -------------------------------------------------------------------------------- /src/types/core.js: -------------------------------------------------------------------------------- 1 | //@flow 2 | import { Left, Right, isLeft, isRight, toString } from '../fp/index.js'; 3 | import type { Either } from '../fp/index.js'; 4 | 5 | export const identity = (a: A): A => a; 6 | 7 | export type ContextEntry = { 8 | +key: string, 9 | +type: Decoder 10 | }; 11 | export type Context = $ReadOnlyArray; 12 | export class ValidationError extends Error { 13 | +value: mixed; 14 | +context: Context; 15 | message: string; 16 | constructor(value: mixed, context: Context, message?: string) { 17 | super(); 18 | this.value = value; 19 | this.context = context; 20 | this.message = message || `Invalid value ${toString(value)} supplied to ${getContextPath(context)}`; 21 | workaroundExtendBuiltins(this, ValidationError); 22 | } 23 | } 24 | export class AggregateError extends Array { 25 | constructor(...args: ValidationError[]) { 26 | super(...args); 27 | workaroundExtendBuiltins(this, AggregateError); 28 | } 29 | messages(): Array { 30 | return [...this.map(e => e.message)]; 31 | } 32 | } 33 | export type Validation = Either; 34 | 35 | export type Predicate = (val: A) => boolean; 36 | export type Is = (m: mixed) => boolean; 37 | export type Validate<-I, A> = (i: I, context: Context) => Validation; 38 | export type Decode<-I, A> = (i: I) => Validation; 39 | export type Encode = (a: A) => O; 40 | export type AnyFlowType = Type; 41 | export type MixedFlowType = Type; 42 | export type TypeOf = $PropertyType; 43 | export type InputOf = $PropertyType; 44 | export type OutputOf = $PropertyType; 45 | 46 | export interface Decoder<-I, A> { 47 | +name: string; 48 | +validate: Validate; 49 | +decode: Decode; 50 | } 51 | 52 | export interface Encoder { 53 | +encode: Encode; 54 | } 55 | 56 | export class Type implements Decoder, Encoder { 57 | +_A: A; 58 | +_O: O; 59 | +_I: I; 60 | +name: string; 61 | +is: Is; 62 | +validate: Validate; 63 | +encode: Encode; 64 | constructor( 65 | /** a unique name for this runtime type */ 66 | name: string, 67 | /** a custom type guard */ 68 | is: Is, 69 | /** succeeds if a value of type I can be decoded to a value of type A */ 70 | validate?: Validate = (m, c) => (is(m) ? success((m: any)) : failure(m, c)), 71 | /** converts a value of type A to a value of type O */ 72 | encode?: Encode = (identity: any) 73 | ) { 74 | this.name = name; 75 | this.is = is; 76 | this.validate = validate; 77 | this.encode = encode; 78 | } 79 | /** a version of `validate` with a default context */ 80 | decode(i: I): Validation { 81 | return this.validate(i, getDefaultContext(this)); 82 | } 83 | /** a version of `validate` which will throw if unsuccessful */ 84 | assert(i: I): A { 85 | const result = this.validate(i, getDefaultContext(this)); 86 | if (isRight(result)) { 87 | return result.value; 88 | } 89 | throw result.value; 90 | } 91 | getAssert(): I => A { 92 | return (i: I) => this.assert(i); 93 | } 94 | pipe(ab: Type, name?: string): Type { 95 | const custEncode = b => this.encode(ab.encode(b)); 96 | const encode = this.encode === identity && ab.encode === identity ? (identity: any) : custEncode; 97 | return new Type( 98 | name || `pipe(${this.name}, ${ab.name})`, 99 | ab.is, 100 | (i, c) => { 101 | const validation = this.validate(i, c); 102 | if (isLeft(validation)) { 103 | return (validation: any); 104 | } else { 105 | return ab.validate(validation.value, c); 106 | } 107 | }, 108 | encode 109 | ); 110 | } 111 | asDecoder(): Decoder { 112 | return this; 113 | } 114 | asEncoder(): Encoder { 115 | return this; 116 | } 117 | } 118 | 119 | export const getDefaultContext = (type: Decoder): Context => ([{ key: '', type }]: Context); 120 | 121 | export const appendContext = (c: Context, key: string, type: Decoder): Context => { 122 | const len = c.length; 123 | const r = Array(len + 1); 124 | for (let i = 0; i < len; i++) { 125 | r[i] = c[i]; 126 | } 127 | r[len] = { key, type }; 128 | return (r: Context); 129 | }; 130 | 131 | export const failures = (errors: AggregateError): Validation => new Left(errors); 132 | 133 | export const failure = (value: mixed, context: Context, message?: string): Validation => { 134 | const errs = new AggregateError(); 135 | errs.push(new ValidationError(value, context, message)); 136 | return failures(errs); 137 | }; 138 | 139 | export const success = (value: T): Validation => new Right(value); 140 | 141 | export const useIdentity = (types: $ReadOnlyArray): boolean => { 142 | return types.every(type => type.encode === identity); 143 | }; 144 | 145 | export function values(val: P): Array<$Values

> { 146 | return Object.values(val); 147 | } 148 | export function mapValues, $Keys

) => mixed>( 149 | vals: P, 150 | iteratee: F 151 | ): Object { 152 | const result = {}; 153 | Object.keys(vals).forEach(key => { 154 | result[key] = iteratee(vals[key], key); 155 | }); 156 | return result; 157 | } 158 | 159 | function getContextPath(context: Context): string { 160 | return context.map(({ key, type }) => `${key}: ${type.name}`).join('/'); 161 | } 162 | 163 | function workaroundExtendBuiltins(context, subclass) { 164 | // Temporary workaround for https://github.com/istanbuljs/babel-plugin-istanbul/issues/143 #TODO 165 | /* eslint-disable no-proto */ 166 | // $FlowExpectError 167 | context.constructor = subclass; 168 | // $FlowExpectError 169 | context.__proto__ = subclass.prototype; 170 | /* eslint-enable */ 171 | } 172 | -------------------------------------------------------------------------------- /test/types/array.js: -------------------------------------------------------------------------------- 1 | //@flow 2 | import * as assert from 'assert'; 3 | import * as t from '../../src/index'; 4 | import { assertSuccess, assertFailure, assertStrictEqual, assertDeepEqual, DateFromNumber } from '../helpers'; 5 | 6 | describe('array', () => { 7 | it('should properly typecheck values', () => { 8 | const T = t.array(t.Number); 9 | const arr1: t.TypeOf = [1, 2, 3]; 10 | const arr2: t.OutputOf = [1, 2, 3]; 11 | // $FlowExpectError 12 | const arr3: t.TypeOf = 2; 13 | // $FlowExpectError 14 | const arr4: t.TypeOf = ['', '']; 15 | // $FlowExpectError 16 | const arr5: t.OutputOf = [true, false]; 17 | 18 | const T2 = t.array(DateFromNumber); 19 | const arr6: t.TypeOf = [new Date(), new Date()]; 20 | const arr7: t.OutputOf = [1, 2]; 21 | // $FlowExpectError 22 | const arr8: t.TypeOf = [1, 2, 3]; 23 | }); 24 | 25 | it('should succeed validating a valid value', () => { 26 | const T = t.array(t.Number); 27 | assertSuccess(T.decode([])); 28 | assertSuccess(T.decode([1, 2, 3])); 29 | }); 30 | 31 | it('should return the same reference if validation succeeded and nothing changed', () => { 32 | const T = t.array(t.Number); 33 | const value = [1, 2, 3]; 34 | assertStrictEqual(T.decode(value), value); 35 | }); 36 | 37 | it('should return a new reference if validation succeeded and something changed', () => { 38 | const T = t.array(DateFromNumber); 39 | assertDeepEqual(T.decode([1, 2, 3]), [new Date(1), new Date(2), new Date(3)]); 40 | }); 41 | 42 | it('should fail validating an invalid value', () => { 43 | const T = t.array(t.Number); 44 | 45 | assertFailure(T.decode(1), ['Invalid value 1 supplied to : Array']); 46 | assertFailure(T.decode([1, 's', 3]), ['Invalid value "s" supplied to : Array/1: number']); 47 | }); 48 | 49 | it('should serialize a deserialized', () => { 50 | const T = t.array(DateFromNumber); 51 | assert.deepEqual(T.encode([new Date(0), new Date(1)]), [0, 1]); 52 | }); 53 | 54 | it('should return the same reference when serializing', () => { 55 | const T = t.array(t.Number); 56 | assert.strictEqual(T.encode, t.identity); 57 | }); 58 | 59 | it('should type guard', () => { 60 | const T1 = t.array(t.Number); 61 | assert.strictEqual(T1.is([]), true); 62 | assert.strictEqual(T1.is([0]), true); 63 | assert.strictEqual(T1.is([0, 'foo']), false); 64 | const T2 = t.array(DateFromNumber); 65 | assert.strictEqual(T2.is([]), true); 66 | assert.strictEqual(T2.is([new Date(0)]), true); 67 | assert.strictEqual(T2.is([new Date(0), 0]), false); 68 | }); 69 | 70 | it('should assign a default name', () => { 71 | const T1 = t.array(t.Number); 72 | assert.strictEqual(T1.name, 'Array'); 73 | const T2 = t.array(t.Number, 'T2'); 74 | assert.strictEqual(T2.name, 'T2'); 75 | }); 76 | }); 77 | 78 | describe('readonlyArray', () => { 79 | it('should properly typecheck values', () => { 80 | const T = t.readonlyArray(t.Number); 81 | const arr1: t.TypeOf = [1, 2, 3]; 82 | const arr2: t.OutputOf = [1, 2, 3]; 83 | // $FlowExpectError 84 | const arr3: t.TypeOf = 2; 85 | // $FlowExpectError 86 | const arr4: t.TypeOf = ['', '']; 87 | // $FlowExpectError 88 | const arr5: t.OutputOf = [true, false]; 89 | 90 | const T2 = t.readonlyArray(DateFromNumber); 91 | const arr6: t.TypeOf = [new Date(), new Date()]; 92 | const arr8: t.OutputOf = [1, 2]; 93 | // $FlowExpectError 94 | const arr9: t.TypeOf = [1, 2, 3]; 95 | }); 96 | 97 | it('should succeed validating a valid value', () => { 98 | const T = t.readonlyArray(t.Number); 99 | assertSuccess(T.decode([1])); 100 | }); 101 | 102 | it('should fail validating an invalid value', () => { 103 | const T = t.readonlyArray(t.Number); 104 | assertFailure(T.decode(['s']), ['Invalid value "s" supplied to : $ReadOnlyArray/0: number']); 105 | }); 106 | 107 | it('should freeze the value', () => { 108 | const T = t.readonlyArray(t.Number); 109 | T.decode([1]).map(x => assert.ok(Object.isFrozen(x))); 110 | }); 111 | 112 | it('should not freeze in production', () => { 113 | const env = process.env.NODE_ENV; 114 | process.env.NODE_ENV = 'production'; 115 | const T = t.readonlyArray(t.Number); 116 | T.decode([1]).map(x => assert.ok(!Object.isFrozen(x))); 117 | process.env.NODE_ENV = env; 118 | }); 119 | 120 | it('should serialize a deserialized', () => { 121 | const T = t.readonlyArray(DateFromNumber); 122 | assert.deepEqual(T.encode([new Date(0), new Date(1)]), [0, 1]); 123 | }); 124 | 125 | it('should return the same reference when serializing', () => { 126 | const T = t.readonlyArray(t.Number); 127 | assert.strictEqual(T.encode, t.identity); 128 | }); 129 | 130 | it('should type guard', () => { 131 | const T1 = t.readonlyArray(t.Number); 132 | assert.strictEqual(T1.is([]), true); 133 | assert.strictEqual(T1.is([0]), true); 134 | assert.strictEqual(T1.is([0, 'foo']), false); 135 | assert.strictEqual(T1.is(undefined), false); 136 | const T2 = t.readonlyArray(DateFromNumber); 137 | assert.strictEqual(T2.is([]), true); 138 | assert.strictEqual(T2.is([new Date(0)]), true); 139 | assert.strictEqual(T2.is([new Date(0), 'foo']), false); 140 | assert.strictEqual(T2.is(undefined), false); 141 | }); 142 | 143 | it('should assign a default name', () => { 144 | const T1 = t.readonlyArray(t.Number); 145 | assert.strictEqual(T1.name, '$ReadOnlyArray'); 146 | const T2 = t.readonlyArray(t.Number, 'T2'); 147 | assert.strictEqual(T2.name, 'T2'); 148 | }); 149 | }); 150 | -------------------------------------------------------------------------------- /test/types/dictionary.js: -------------------------------------------------------------------------------- 1 | //@flow 2 | import * as assert from 'assert'; 3 | import * as t from '../../src/index'; 4 | import { assertSuccess, assertFailure, assertStrictEqual, assertDeepEqual, string2, DateFromNumber } from '../helpers'; 5 | 6 | describe('dictionary', () => { 7 | it('should properly typecheck values', () => { 8 | /** Basic Dictionary **/ 9 | const T = t.dictionary(t.String, t.Number); 10 | const a: t.TypeOf = { a: 1, b: 2 }; 11 | const b: t.OutputOf = { c: 3, d: 4 }; 12 | // $FlowExpectError 13 | const c: t.TypeOf = { a: 'hi', b: '3' }; 14 | // $FlowExpectError 15 | const d: t.TypeOf = 1; 16 | // $FlowExpectError 17 | const e: t.OutputOf = [1, 2, 3]; 18 | 19 | /** Basic Dictionary with Literal Support **/ 20 | const fooLiteral = t.literal<'foo'>('foo'); 21 | const T2 = t.dictionary(fooLiteral, t.String); 22 | const f: t.TypeOf = { foo: 'hi' }; 23 | // $FlowExpectError 24 | const g: t.TypeOf = { foo2: 'hi' }; 25 | 26 | /** Dictionary with Complex Output **/ 27 | const T3 = t.dictionary(t.String, DateFromNumber); 28 | const h: t.TypeOf = { foo: new Date() }; 29 | const i: t.OutputOf = { foo: 1 }; 30 | // $FlowExpectError 31 | const j: t.TypeOf = { foo: 1 }; 32 | }); 33 | 34 | it('should succeed validating a valid value', () => { 35 | const T1 = t.dictionary(t.String, t.Number); 36 | assertSuccess(T1.decode({})); 37 | assertSuccess(T1.decode({ aa: 1 })); 38 | const T2 = t.dictionary(t.refinement(t.String, s => s.length >= 2), t.Number); 39 | assertSuccess(T2.decode({})); 40 | assertSuccess(T2.decode({ aa: 1 })); 41 | const T3 = t.dictionary(string2, t.Number); 42 | assertSuccess(T3.decode({})); 43 | assertSuccess(T3.decode({ aa: 1 })); 44 | // const T4 = t.dictionary(t.String, t.any); 45 | // assertSuccess(T4.decode([])); 46 | // assertSuccess(T4.decode([1])); 47 | // assertSuccess(T4.decode(new Number())); 48 | // assertSuccess(T4.decode(new Date())); 49 | }); 50 | 51 | it('should return the same reference if validation succeeded if nothing changed', () => { 52 | const T1 = t.dictionary(t.String, t.Number); 53 | const value1 = { aa: 1 }; 54 | assertStrictEqual(T1.decode(value1), value1); 55 | const T2 = t.dictionary(t.refinement(t.String, s => s.length >= 2), t.Number); 56 | const value2 = { aa: 1 }; 57 | assertStrictEqual(T2.decode(value2), value2); 58 | }); 59 | 60 | it('should return a new reference if validation succeeded and something changed', () => { 61 | const T = t.dictionary(string2, t.Number); 62 | const value = { aa: 1 }; 63 | assertDeepEqual(T.decode(value), { 'a-a': 1 }); 64 | }); 65 | 66 | it('should fail validating an invalid value', () => { 67 | const T1 = t.dictionary(t.String, t.Number); 68 | assertFailure(T1.decode(1), ['Invalid value 1 supplied to : { [K: string]: number }']); 69 | assertFailure(T1.decode({ aa: 's' }), ['Invalid value "s" supplied to : { [K: string]: number }/aa: number']); 70 | assertFailure(T1.decode([]), ['Invalid value [] supplied to : { [K: string]: number }']); 71 | assertFailure(T1.decode([1]), ['Invalid value [1] supplied to : { [K: string]: number }']); 72 | assertFailure(T1.decode(new Number()), ['Invalid value 0 supplied to : { [K: string]: number }']); 73 | const d = new Date(); 74 | assertFailure(T1.decode(d), [`Invalid value ${t.toString(d)} supplied to : { [K: string]: number }`]); 75 | const T2 = t.dictionary(string2, t.Any); 76 | //NOTE: Changed this tests value unexpectedly 77 | assertFailure(T2.decode([1]), ['Invalid value [1] supplied to : { [K: string2]: any }']); 78 | }); 79 | 80 | it('should support literals as domain type', () => { 81 | const fooLiteral = t.literal<'foo'>('foo'); 82 | const T = t.dictionary(fooLiteral, t.String); 83 | assertSuccess(T.decode({ foo: 'bar' })); 84 | assertFailure(T.decode({ foo: 'bar', baz: 'bob' }), [ 85 | 'Invalid value "baz" supplied to : { [K: "foo"]: string }/baz: "foo"' 86 | ]); 87 | }); 88 | 89 | it('should support keyof as domain type', () => { 90 | const keys = t.keyof({ foo: true, bar: true }); 91 | const T = t.dictionary(keys, t.String); 92 | assertSuccess(T.decode({ foo: 'bar' })); 93 | assertFailure(T.decode({ foo: 'bar', baz: 'bob' }), [ 94 | 'Invalid value "baz" supplied to : { [K: (keyof ["foo","bar"])]: string }/baz: (keyof ["foo","bar"])' 95 | ]); 96 | }); 97 | 98 | it('should serialize a deserialized', () => { 99 | const T1 = t.dictionary(t.String, DateFromNumber); 100 | assert.deepEqual(T1.encode({ a: new Date(0), b: new Date(1) }), { a: 0, b: 1 }); 101 | const T2 = t.dictionary(string2, t.Number); 102 | assert.deepEqual(T2.encode({ 'a-a': 1, 'a-b': 2 }), { aa: 1, ab: 2 }); 103 | }); 104 | 105 | it('should return the same reference when serializing', () => { 106 | const T1 = t.dictionary(t.String, t.Number); 107 | assert.strictEqual(T1.encode, t.identity); 108 | const T2 = t.dictionary(string2, t.Number); 109 | assert.strictEqual(T2.encode === t.identity, false); 110 | }); 111 | 112 | it('should type guard', () => { 113 | const T1 = t.dictionary(t.String, t.Number); 114 | assert.strictEqual(T1.is({}), true); 115 | assert.strictEqual(T1.is({ a: 1 }), true); 116 | assert.strictEqual(T1.is({ a: 'foo' }), false); 117 | const T2 = t.dictionary(t.String, DateFromNumber); 118 | assert.strictEqual(T2.is({}), true); 119 | assert.strictEqual(T2.is({ a: new Date(0) }), true); 120 | assert.strictEqual(T2.is({ a: 0 }), false); 121 | const T3 = t.dictionary(string2, t.Number); 122 | assert.strictEqual(T3.is({}), true); 123 | assert.strictEqual(T3.is({ 'a-a': 1 }), true); 124 | assert.strictEqual(T3.is({ aa: 1 }), false); 125 | }); 126 | 127 | it('should assign a default name', () => { 128 | const T1 = t.dictionary(t.String, t.Number); 129 | assert.strictEqual(T1.name, '{ [K: string]: number }'); 130 | const T2 = t.dictionary(t.String, t.Number, 'T2'); 131 | assert.strictEqual(T2.name, 'T2'); 132 | }); 133 | }); 134 | -------------------------------------------------------------------------------- /test/types/union.js: -------------------------------------------------------------------------------- 1 | //@flow 2 | import * as assert from 'assert'; 3 | import * as t from '../../src/index'; 4 | import { 5 | NumberOrString, 6 | DateFromNumOrStr2, 7 | assertSuccess, 8 | assertFailure, 9 | assertStrictEqual, 10 | DateFromNumber, 11 | DateFromNumOrStr 12 | } from '../helpers'; 13 | 14 | describe('union', () => { 15 | it('should properly typecheck values', () => { 16 | const T = t.union([t.String, DateFromNumber]); 17 | const a: t.TypeOf = 'foo'; 18 | const b: t.TypeOf = new Date(); 19 | const c: t.OutputOf = 'foo'; 20 | const d: t.OutputOf = 1; 21 | // $FlowExpectError 22 | const e: t.TypeOf = 1; 23 | // $FlowExpectError 24 | const f: t.TypeOf = true; 25 | // $FlowExpectError 26 | const g: t.OutputOf = [1, 2]; 27 | }); 28 | 29 | it('should succeed validating a valid value', () => { 30 | const T = t.union([t.String, t.Number]); 31 | assertSuccess(T.decode('s')); 32 | assertSuccess(T.decode(1)); 33 | }); 34 | 35 | it('should return the same reference if validation succeeded', () => { 36 | const value: { [key: string]: mixed } | number = {}; 37 | const T = t.union([t.Dictionary, t.Number]); 38 | assertStrictEqual(T.decode(value), value); 39 | }); 40 | 41 | it('should fail validating an invalid value', () => { 42 | const T = t.union([t.String, t.Number]); 43 | assertFailure(T.decode(true), [ 44 | 'Invalid value true supplied to : (string | number)/0: string', 45 | 'Invalid value true supplied to : (string | number)/1: number' 46 | ]); 47 | }); 48 | 49 | it('should serialize a deserialized', () => { 50 | const T1 = t.union([t.inexactAll({ a: DateFromNumber }), t.Number]); 51 | assert.deepEqual(T1.encode({ a: new Date(0) }), { a: 0 }); 52 | assert.deepEqual(T1.encode(1), 1); 53 | const T2 = t.union([t.Number, DateFromNumber]); 54 | assert.deepEqual(T2.encode(new Date(0)), 0); 55 | }); 56 | 57 | it('should throw when encoding an invalid input', () => { 58 | // $FlowExpectError 59 | assert.throws(() => DateFromNumOrStr2.encode(true)); 60 | }); 61 | 62 | it('should return the same reference when serializing', () => { 63 | const T = t.union([t.exactAll({ a: t.Number }), t.String]); 64 | assert.strictEqual(T.encode, t.identity); 65 | }); 66 | 67 | it('should type guard', () => { 68 | const T1 = t.union([t.String, t.Number]); 69 | assert.strictEqual(T1.is(0), true); 70 | assert.strictEqual(T1.is('foo'), true); 71 | assert.strictEqual(T1.is(true), false); 72 | const T2 = t.union([t.String, DateFromNumber]); 73 | assert.strictEqual(T2.is(new Date(0)), true); 74 | assert.strictEqual(T2.is('foo'), true); 75 | assert.strictEqual(T2.is(true), false); 76 | }); 77 | 78 | it('should assign a default name', () => { 79 | const T1 = t.union([t.String, t.Number]); 80 | assert.strictEqual(T1.name, '(string | number)'); 81 | const T2 = t.union([t.String, t.Number], 'T2'); 82 | assert.strictEqual(T2.name, 'T2'); 83 | }); 84 | }); 85 | 86 | describe('unionMap', () => { 87 | it('should succeed validating a valid value', () => { 88 | const T = t.unionMap({ string: t.String, number: t.Number }, v => typeof v); 89 | assertSuccess(T.decode('s')); 90 | assertSuccess(T.decode(1)); 91 | }); 92 | 93 | it('should return the same reference if validation succeeded', () => { 94 | const Add = t.exactAll({ op: t.literal('Add'), value: t.Number }); 95 | const Subtract = t.exactAll({ op: t.literal('Subtract'), value: t.Number }); 96 | const T = t.unionMap({ Add, Subtract }, 'op'); 97 | const value = { op: 'Add', value: 2 }; 98 | assertStrictEqual(T.decode(value), value); 99 | }); 100 | 101 | it('should fail validating an invalid value', () => { 102 | const Add = t.exactAll({ op: t.literal<'Add'>('Add'), value: t.Number }); 103 | const Subtract = t.exactAll({ op: t.literal<'Subtract'>('Subtract'), value: t.Number }); 104 | const T = t.unionMap({ Add, Subtract }, 'op'); 105 | const value = { op: 'Add', value: '2' }; 106 | const value2 = { op: 'Multiply', value: '3' }; 107 | assertFailure(T.decode(value), [ 108 | 'Invalid value "2" supplied to : ({| op: "Add", value: number |} | {| op: "Subtract", value: number |})/value: number' 109 | ]); 110 | assertFailure(T.decode(value2), [ 111 | 'Invalid value {"op":"Multiply","value":"3"} supplied to : ({| op: "Add", value: number |} | {| op: "Subtract", value: number |})' 112 | ]); 113 | assertFailure(T.decode(3), [ 114 | 'Invalid value 3 supplied to : ({| op: "Add", value: number |} | {| op: "Subtract", value: number |})' 115 | ]); 116 | }); 117 | 118 | it('should serialize a deserialized', () => { 119 | const Add = t.exactAll({ op: t.literal<'Add'>('Add'), value: DateFromNumber }); 120 | const Subtract = t.exactAll({ op: t.literal<'Subtract'>('Subtract'), value: t.Number }); 121 | const T = t.unionMap({ Add, Subtract }, 'op'); 122 | assert.deepEqual(T.encode({ op: 'Add', value: new Date(0) }), { op: 'Add', value: 0 }); 123 | assert.deepEqual(T.encode({ op: 'Subtract', value: 1 }), { op: 'Subtract', value: 1 }); 124 | }); 125 | 126 | it('should return the same reference when serializing', () => { 127 | const T = t.unionMap({ string: t.String, number: t.Number }, v => typeof v); 128 | assert.strictEqual(T.encode, t.identity); 129 | }); 130 | 131 | it('should type guard', () => { 132 | const T1 = t.unionMap({ string: t.String, number: t.Number }, v => typeof v); 133 | assert.strictEqual(T1.is(0), true); 134 | assert.strictEqual(T1.is('foo'), true); 135 | assert.strictEqual(T1.is(true), false); 136 | 137 | const Add = t.exactAll({ op: t.literal<'Add'>('Add'), value: DateFromNumber }); 138 | const Subtract = t.exactAll({ op: t.literal<'Subtract'>('Subtract'), value: t.Number }); 139 | const T2 = t.unionMap({ Add, Subtract }, 'op'); 140 | assert.strictEqual(T2.is({ op: 'Add', value: new Date() }), true); 141 | assert.strictEqual(T2.is({ op: 'Subtract', value: 1 }), true); 142 | assert.strictEqual(T2.is({ op: 'Add', value: 1 }), false); 143 | }); 144 | 145 | it('should assign a default name', () => { 146 | const map = { string: t.String, number: t.Number }; 147 | const T1 = t.unionMap(map, v => typeof v); 148 | assert.strictEqual(T1.name, '(string | number)'); 149 | const T2 = t.unionMap(map, v => typeof v, 'T2'); 150 | assert.strictEqual(T2.name, 'T2'); 151 | }); 152 | 153 | it('should throw on complex serializations with invalid inputs', () => { 154 | // $FlowExpectError 155 | assert.throws(() => DateFromNumOrStr.encode(true)); 156 | }); 157 | }); 158 | -------------------------------------------------------------------------------- /flow-typed/npm/flow-typed_vx.x.x.js: -------------------------------------------------------------------------------- 1 | // flow-typed signature: b47b0ab7ec39e482f4aa8f086684d1b9 2 | // flow-typed version: <>/flow-typed_v2.5.1/flow_v0.86.0 3 | 4 | /** 5 | * This is an autogenerated libdef stub for: 6 | * 7 | * 'flow-typed' 8 | * 9 | * Fill this stub out by replacing all the `any` types. 10 | * 11 | * Once filled out, we encourage you to share your work with the 12 | * community by sending a pull request to: 13 | * https://github.com/flowtype/flow-typed 14 | */ 15 | 16 | declare module 'flow-typed' { 17 | declare module.exports: any; 18 | } 19 | 20 | /** 21 | * We include stubs for each file inside this npm package in case you need to 22 | * require those files directly. Feel free to delete any files that aren't 23 | * needed. 24 | */ 25 | declare module 'flow-typed/dist/cli' { 26 | declare module.exports: any; 27 | } 28 | 29 | declare module 'flow-typed/dist/commands/create-stub' { 30 | declare module.exports: any; 31 | } 32 | 33 | declare module 'flow-typed/dist/commands/install' { 34 | declare module.exports: any; 35 | } 36 | 37 | declare module 'flow-typed/dist/commands/runTests' { 38 | declare module.exports: any; 39 | } 40 | 41 | declare module 'flow-typed/dist/commands/search' { 42 | declare module.exports: any; 43 | } 44 | 45 | declare module 'flow-typed/dist/commands/update-cache' { 46 | declare module.exports: any; 47 | } 48 | 49 | declare module 'flow-typed/dist/commands/update' { 50 | declare module.exports: any; 51 | } 52 | 53 | declare module 'flow-typed/dist/commands/validateDefs' { 54 | declare module.exports: any; 55 | } 56 | 57 | declare module 'flow-typed/dist/commands/version' { 58 | declare module.exports: any; 59 | } 60 | 61 | declare module 'flow-typed/dist/lib/cacheRepoUtils' { 62 | declare module.exports: any; 63 | } 64 | 65 | declare module 'flow-typed/dist/lib/codeSign' { 66 | declare module.exports: any; 67 | } 68 | 69 | declare module 'flow-typed/dist/lib/fileUtils' { 70 | declare module.exports: any; 71 | } 72 | 73 | declare module 'flow-typed/dist/lib/flowProjectUtils' { 74 | declare module.exports: any; 75 | } 76 | 77 | declare module 'flow-typed/dist/lib/flowVersion' { 78 | declare module.exports: any; 79 | } 80 | 81 | declare module 'flow-typed/dist/lib/git' { 82 | declare module.exports: any; 83 | } 84 | 85 | declare module 'flow-typed/dist/lib/github' { 86 | declare module.exports: any; 87 | } 88 | 89 | declare module 'flow-typed/dist/lib/isInFlowTypedRepo' { 90 | declare module.exports: any; 91 | } 92 | 93 | declare module 'flow-typed/dist/lib/libDefs' { 94 | declare module.exports: any; 95 | } 96 | 97 | declare module 'flow-typed/dist/lib/node' { 98 | declare module.exports: any; 99 | } 100 | 101 | declare module 'flow-typed/dist/lib/npm/npmLibDefs' { 102 | declare module.exports: any; 103 | } 104 | 105 | declare module 'flow-typed/dist/lib/npm/npmProjectUtils' { 106 | declare module.exports: any; 107 | } 108 | 109 | declare module 'flow-typed/dist/lib/semver' { 110 | declare module.exports: any; 111 | } 112 | 113 | declare module 'flow-typed/dist/lib/stubUtils' { 114 | declare module.exports: any; 115 | } 116 | 117 | declare module 'flow-typed/dist/lib/validationErrors' { 118 | declare module.exports: any; 119 | } 120 | 121 | declare module 'flow-typed/flow-typed/npm/unzipper_vx.x.x' { 122 | declare module.exports: any; 123 | } 124 | 125 | // Filename aliases 126 | declare module 'flow-typed/dist/cli.js' { 127 | declare module.exports: $Exports<'flow-typed/dist/cli'>; 128 | } 129 | declare module 'flow-typed/dist/commands/create-stub.js' { 130 | declare module.exports: $Exports<'flow-typed/dist/commands/create-stub'>; 131 | } 132 | declare module 'flow-typed/dist/commands/install.js' { 133 | declare module.exports: $Exports<'flow-typed/dist/commands/install'>; 134 | } 135 | declare module 'flow-typed/dist/commands/runTests.js' { 136 | declare module.exports: $Exports<'flow-typed/dist/commands/runTests'>; 137 | } 138 | declare module 'flow-typed/dist/commands/search.js' { 139 | declare module.exports: $Exports<'flow-typed/dist/commands/search'>; 140 | } 141 | declare module 'flow-typed/dist/commands/update-cache.js' { 142 | declare module.exports: $Exports<'flow-typed/dist/commands/update-cache'>; 143 | } 144 | declare module 'flow-typed/dist/commands/update.js' { 145 | declare module.exports: $Exports<'flow-typed/dist/commands/update'>; 146 | } 147 | declare module 'flow-typed/dist/commands/validateDefs.js' { 148 | declare module.exports: $Exports<'flow-typed/dist/commands/validateDefs'>; 149 | } 150 | declare module 'flow-typed/dist/commands/version.js' { 151 | declare module.exports: $Exports<'flow-typed/dist/commands/version'>; 152 | } 153 | declare module 'flow-typed/dist/lib/cacheRepoUtils.js' { 154 | declare module.exports: $Exports<'flow-typed/dist/lib/cacheRepoUtils'>; 155 | } 156 | declare module 'flow-typed/dist/lib/codeSign.js' { 157 | declare module.exports: $Exports<'flow-typed/dist/lib/codeSign'>; 158 | } 159 | declare module 'flow-typed/dist/lib/fileUtils.js' { 160 | declare module.exports: $Exports<'flow-typed/dist/lib/fileUtils'>; 161 | } 162 | declare module 'flow-typed/dist/lib/flowProjectUtils.js' { 163 | declare module.exports: $Exports<'flow-typed/dist/lib/flowProjectUtils'>; 164 | } 165 | declare module 'flow-typed/dist/lib/flowVersion.js' { 166 | declare module.exports: $Exports<'flow-typed/dist/lib/flowVersion'>; 167 | } 168 | declare module 'flow-typed/dist/lib/git.js' { 169 | declare module.exports: $Exports<'flow-typed/dist/lib/git'>; 170 | } 171 | declare module 'flow-typed/dist/lib/github.js' { 172 | declare module.exports: $Exports<'flow-typed/dist/lib/github'>; 173 | } 174 | declare module 'flow-typed/dist/lib/isInFlowTypedRepo.js' { 175 | declare module.exports: $Exports<'flow-typed/dist/lib/isInFlowTypedRepo'>; 176 | } 177 | declare module 'flow-typed/dist/lib/libDefs.js' { 178 | declare module.exports: $Exports<'flow-typed/dist/lib/libDefs'>; 179 | } 180 | declare module 'flow-typed/dist/lib/node.js' { 181 | declare module.exports: $Exports<'flow-typed/dist/lib/node'>; 182 | } 183 | declare module 'flow-typed/dist/lib/npm/npmLibDefs.js' { 184 | declare module.exports: $Exports<'flow-typed/dist/lib/npm/npmLibDefs'>; 185 | } 186 | declare module 'flow-typed/dist/lib/npm/npmProjectUtils.js' { 187 | declare module.exports: $Exports<'flow-typed/dist/lib/npm/npmProjectUtils'>; 188 | } 189 | declare module 'flow-typed/dist/lib/semver.js' { 190 | declare module.exports: $Exports<'flow-typed/dist/lib/semver'>; 191 | } 192 | declare module 'flow-typed/dist/lib/stubUtils.js' { 193 | declare module.exports: $Exports<'flow-typed/dist/lib/stubUtils'>; 194 | } 195 | declare module 'flow-typed/dist/lib/validationErrors.js' { 196 | declare module.exports: $Exports<'flow-typed/dist/lib/validationErrors'>; 197 | } 198 | declare module 'flow-typed/flow-typed/npm/unzipper_vx.x.x.js' { 199 | declare module.exports: $Exports<'flow-typed/flow-typed/npm/unzipper_vx.x.x'>; 200 | } 201 | -------------------------------------------------------------------------------- /test/types/props/inexact.js: -------------------------------------------------------------------------------- 1 | //@flow 2 | import * as assert from 'assert'; 3 | import * as t from '../../../src/index'; 4 | import { assertSuccess, assertFailure, assertStrictEqual, assertDeepEqual, DateFromNumber } from '../../helpers'; 5 | 6 | describe('inexact', () => { 7 | it('should properly typecheck values', () => { 8 | /** Inexact **/ 9 | const required = { foo: t.String }; 10 | const optional = { bar: DateFromNumber }; 11 | const T = t.inexact({ required, optional }); 12 | const a: t.TypeOf = { foo: 'hi', bar: new Date() }; 13 | const b: t.OutputOf = { foo: 'hi', bar: 1 }; 14 | const c: t.TypeOf = { foo: 'hi', bar: new Date(), extra: 1 }; 15 | // $FlowExpectError 16 | const d: t.TypeOf = { foo: 1, bar: 1 }; 17 | // $FlowExpectError 18 | const e: t.TypeOf = ({}: {||}); 19 | // $FlowExpectError 20 | const f: t.TypeOf = 1; 21 | 22 | const T2 = T.shape(); 23 | const g: t.TypeOf = { foo: 'hi' }; 24 | const h: t.OutputOf = { bar: 1 }; 25 | const i: t.TypeOf = {}; 26 | const j: t.TypeOf = { foo: 'hi', bar: new Date(), extra: 1 }; 27 | const k: t.OutputOf = { foo: 'hi', bar: 1, extra: 1 }; 28 | // $FlowExpectError 29 | const l: t.TypeOf = { foo: 1, bar: 1 }; 30 | // $FlowExpectError 31 | const m: t.TypeOf = 1; 32 | 33 | const T3 = T.readOnly(); 34 | const n: t.TypeOf = { foo: 'hi' }; 35 | const o: t.OutputOf = { foo: 'hi', bar: 1 }; 36 | const p: t.TypeOf = { foo: 'hi', bar: new Date(), extra: 1 }; 37 | // $FlowExpectError 38 | const q: t.OutputOf = { foo: 1, bar: 1, extra: 1 }; 39 | // $FlowExpectError 40 | const s: t.TypeOf = 1; 41 | 42 | const T4 = T.exact(); 43 | const u: t.TypeOf = { foo: 'hi' }; 44 | const v: t.OutputOf = { foo: 'hi', bar: 1 }; 45 | // $FlowExpectError 46 | const w: t.TypeOf = { foo: 'hi', bar: new Date(), extra: 1 }; 47 | // $FlowExpectError 48 | const x: t.OutputOf = { foo: 'hi', bar: 1, extra: 1 }; 49 | // $FlowExpectError 50 | const y: t.TypeOf = { foo: 1, bar: new Date() }; 51 | // $FlowExpectError 52 | const z: t.TypeOf = 1; 53 | }); 54 | it('should succeed validating a valid value (required + optional)', () => { 55 | const required = { a: t.String }; 56 | const optional = { b: t.Number }; 57 | const T = t.inexact({ required, optional }); 58 | assertSuccess(T.decode({ a: 's' })); 59 | }); 60 | 61 | it('should succeed validating a valid value (required)', () => { 62 | const required = { a: t.String }; 63 | const T = t.inexactAll(required); 64 | assertSuccess(T.decode({ a: 's' })); 65 | }); 66 | 67 | it('should succeed validating a valid value (shape)', () => { 68 | const optional = { b: t.Number }; 69 | const T = t.inexactShape(optional); 70 | assertSuccess(T.decode({})); 71 | assertSuccess(T.decode({ b: 1 })); 72 | }); 73 | 74 | it('should succeed validating a valid value', () => { 75 | const T = t.inexactAll({ a: t.String }); 76 | assertSuccess(T.decode({ a: 's' })); 77 | }); 78 | 79 | it('should keep unknown properties', () => { 80 | const T = t.inexactAll({ a: t.String }); 81 | const validation = T.decode({ a: 's', b: 1 }); 82 | if (validation.isRight()) { 83 | assert.deepEqual(validation.value, { a: 's', b: 1 }); 84 | } else { 85 | assert.ok(false); 86 | } 87 | }); 88 | 89 | it('should return the same reference if validation succeeded and nothing changed', () => { 90 | const T = t.inexactAll({ a: t.String }); 91 | const value = { a: 's' }; 92 | assertStrictEqual(T.decode(value), value); 93 | }); 94 | 95 | it('should return the a new reference if validation succeeded and something changed', () => { 96 | const T = t.inexactAll({ a: DateFromNumber, b: t.Number }); 97 | assertDeepEqual(T.decode({ a: 1, b: 2 }), { a: new Date(1), b: 2 }); 98 | }); 99 | 100 | it('should fail validating an invalid value', () => { 101 | const T = t.inexactAll({ a: t.String }); 102 | assertFailure(T.decode(1), ['Invalid value 1 supplied to : { a: string }']); 103 | assertFailure(T.decode({}), ['Invalid value undefined supplied to : { a: string }/a: string']); 104 | assertFailure(T.decode({ a: 1 }), ['Invalid value 1 supplied to : { a: string }/a: string']); 105 | }); 106 | 107 | it('should serialize a deserialized', () => { 108 | const T = t.inexactAll({ a: DateFromNumber }); 109 | assert.deepEqual(T.encode({ a: new Date(0) }), { a: 0 }); 110 | }); 111 | 112 | it('should return the same reference when serializing', () => { 113 | const T = t.inexactAll({ a: t.Number }); 114 | assert.strictEqual(T.encode, t.identity); 115 | }); 116 | 117 | it('should succeed validating a valid value (readonly)', () => { 118 | const T = t.inexactAll({ foo: t.String }).readOnly(); 119 | assertSuccess(T.decode({ foo: 'foo' })); 120 | }); 121 | 122 | it('should fail validating an invalid value (readonly)', () => { 123 | const T = t.inexactAll({ foo: t.String }).readOnly(); 124 | assertFailure(T.decode({ foo: 1, bar: 1 }), ['Invalid value 1 supplied to : { +foo: string }/foo: string']); 125 | }); 126 | 127 | it('should type guard', () => { 128 | const T1 = t.inexactAll({ a: t.Number }); 129 | assert.strictEqual(T1.is({ a: 0 }), true); 130 | assert.strictEqual(T1.is(undefined), false); 131 | const T2 = t.inexactAll({ a: DateFromNumber }); 132 | assert.strictEqual(T2.is({ a: new Date(0) }), true); 133 | assert.strictEqual(T2.is({ a: 0 }), false); 134 | assert.strictEqual(T2.is(undefined), false); 135 | }); 136 | 137 | it('should preserve additional properties while encoding', () => { 138 | const T = t.inexactAll({ a: DateFromNumber }); 139 | const x = { a: new Date(0), b: 'foo' }; 140 | assert.deepEqual(T.encode(x), { a: 0, b: 'foo' }); 141 | }); 142 | 143 | it('should preserve additional properties while encoding', () => { 144 | const T = t.inexactAll({ a: DateFromNumber }); 145 | const x = { a: new Date(0), b: 'foo' }; 146 | assert.deepEqual(T.encode(x), { a: 0, b: 'foo' }); 147 | }); 148 | 149 | it('should be convertable to exact type', () => { 150 | const T = t.inexactAll({ a: DateFromNumber }); 151 | const x = { a: 0, b: 'foo' }; 152 | assertSuccess(T.decode(x)); 153 | const T2 = T.exact(); 154 | assertFailure(T2.decode(x), ['Invalid value "foo" supplied to : {| a: DateFromNumber |}/b: never']); 155 | }); 156 | 157 | it('should be convertable to $Shape type', () => { 158 | const T = t.inexactAll({ a: DateFromNumber }); 159 | const x = {}; 160 | assertFailure(T.decode(x), ['Invalid value undefined supplied to : { a: DateFromNumber }/a: DateFromNumber']); 161 | const T2 = T.shape(); 162 | assertSuccess(T2.decode(x)); 163 | assertSuccess(T2.decode({ a: 1 })); 164 | assertFailure(T2.decode({ a: 'hi' }), [ 165 | 'Invalid value "hi" supplied to : { a?: DateFromNumber }/a: DateFromNumber' 166 | ]); 167 | }); 168 | }); 169 | -------------------------------------------------------------------------------- /test/types/basic.js: -------------------------------------------------------------------------------- 1 | //@flow 2 | import * as assert from 'assert'; 3 | import * as t from '../../src/index'; 4 | import { assertSuccess, assertFailure } from '../helpers'; 5 | 6 | describe('Dictionary', () => { 7 | it('should properly typecheck values', () => { 8 | const T = t.Dictionary; 9 | const a: t.TypeOf = {}; 10 | const b: t.OutputOf = {}; 11 | // $FlowExpectError 12 | const c: t.TypeOf = []; 13 | // $FlowExpectError 14 | const d: t.OutputOf = true; 15 | }); 16 | 17 | it('should succeed validating a valid value', () => { 18 | const T = t.Dictionary; 19 | assertSuccess(T.decode({})); 20 | // assertSuccess(T.decode([])); 21 | // assertSuccess(T.decode([1])); 22 | // assertSuccess(T.decode(new Number())); 23 | // assertSuccess(T.decode(new Date())); 24 | }); 25 | 26 | it('should fail validating an invalid value', () => { 27 | const T = t.Dictionary; 28 | assertFailure(T.decode('s'), ['Invalid value "s" supplied to : Dictionary']); 29 | assertFailure(T.decode(1), ['Invalid value 1 supplied to : Dictionary']); 30 | assertFailure(T.decode(true), ['Invalid value true supplied to : Dictionary']); 31 | assertFailure(T.decode(null), ['Invalid value null supplied to : Dictionary']); 32 | assertFailure(T.decode(undefined), ['Invalid value undefined supplied to : Dictionary']); 33 | }); 34 | }); 35 | 36 | describe('Integer', () => { 37 | it('should properly typecheck values', () => { 38 | // $FlowExpectError 39 | const a: t.TypeOf = 1.5; 40 | // $FlowExpectError 41 | const b: t.TypeOf = 1; 42 | const validC = t.Integer.decode(1); 43 | if (validC.tag === 'Right') { 44 | const c: t.TypeOf = validC.value; 45 | } 46 | }); 47 | 48 | it('should validate integers', () => { 49 | assertSuccess(t.Integer.decode(1)); 50 | assertFailure(t.Integer.decode(0.5), ['Invalid value 0.5 supplied to : Integer']); 51 | assertFailure(t.Integer.decode('foo'), ['Invalid value "foo" supplied to : Integer']); 52 | }); 53 | }); 54 | 55 | describe('null', () => { 56 | it('should properly typecheck values', () => { 57 | const T = t.Null; 58 | const a: t.TypeOf = null; 59 | const b: t.OutputOf = null; 60 | // $FlowExpectError 61 | const c: t.TypeOf = []; 62 | // $FlowExpectError 63 | const d: t.OutputOf = true; 64 | }); 65 | it('should support the alias `nullType`', () => { 66 | assertSuccess(t.Null.decode(null)); 67 | assertFailure(t.Null.decode(1), ['Invalid value 1 supplied to : null']); 68 | }); 69 | }); 70 | 71 | describe('void', () => { 72 | it('should properly typecheck values', () => { 73 | const T = t.Void; 74 | const a: t.TypeOf = undefined; 75 | const b: t.TypeOf = void 0; 76 | const c: t.OutputOf = undefined; 77 | // $FlowExpectError 78 | const d: t.TypeOf = []; 79 | // $FlowExpectError 80 | const e: t.OutputOf = true; 81 | }); 82 | it('should support the alias `voidType`', () => { 83 | assertSuccess(t.Void.decode(undefined)); 84 | assertFailure(t.Void.decode(1), ['Invalid value 1 supplied to : void']); 85 | }); 86 | }); 87 | 88 | describe('object', () => { 89 | it('should properly typecheck values', () => { 90 | const T = t.object; 91 | const a: t.TypeOf = {}; 92 | const b: t.OutputOf = {}; 93 | }); 94 | 95 | it('should decode arrays', () => { 96 | assertSuccess(t.object.decode([])); 97 | }); 98 | 99 | it('should decode objects', () => { 100 | assertSuccess(t.object.decode({})); 101 | }); 102 | 103 | it('should fail with primitives', () => { 104 | const T = t.object; 105 | assertFailure(T.decode('s'), ['Invalid value "s" supplied to : Object']); 106 | assertFailure(T.decode(1), ['Invalid value 1 supplied to : Object']); 107 | assertFailure(T.decode(true), ['Invalid value true supplied to : Object']); 108 | }); 109 | 110 | it('should fail with null and undefined', () => { 111 | const T = t.object; 112 | assertFailure(T.decode(null), ['Invalid value null supplied to : Object']); 113 | assertFailure(T.decode(undefined), ['Invalid value undefined supplied to : Object']); 114 | }); 115 | }); 116 | 117 | describe('any', () => { 118 | it('should properly typecheck values', () => { 119 | const T = t.Any; 120 | const a: t.TypeOf = 1; 121 | const b: t.OutputOf = []; 122 | }); 123 | 124 | it('should decode any value', () => { 125 | assertSuccess(t.Any.decode(null)); 126 | assertSuccess(t.Any.decode(undefined)); 127 | assertSuccess(t.Any.decode('foo')); 128 | assertSuccess(t.Any.decode(1)); 129 | assertSuccess(t.Any.decode(true)); 130 | assertSuccess(t.Any.decode(t.identity)); 131 | assertSuccess(t.Any.decode({})); 132 | assertSuccess(t.Any.decode([])); 133 | assertSuccess(t.Any.decode(/a/)); 134 | }); 135 | 136 | it('should accept any value', () => { 137 | assert.ok(t.Any.is(null)); 138 | assert.ok(t.Any.is(undefined)); 139 | assert.ok(t.Any.is('foo')); 140 | assert.ok(t.Any.is(1)); 141 | assert.ok(t.Any.is(true)); 142 | assert.ok(t.Any.is(t.identity)); 143 | assert.ok(t.Any.is({})); 144 | assert.ok(t.Any.is([])); 145 | assert.ok(t.Any.is(/a/)); 146 | }); 147 | }); 148 | 149 | describe('never', () => { 150 | it('should properly typecheck values', () => { 151 | const T = t.Never; 152 | // $FlowExpectError 153 | const a: t.TypeOf = 1; 154 | // $FlowExpectError 155 | const b: t.OutputOf = []; 156 | }); 157 | it('should not decode any value', () => { 158 | assertFailure(t.Never.decode(null), ['Invalid value null supplied to : never']); 159 | assertFailure(t.Never.decode(undefined), ['Invalid value undefined supplied to : never']); 160 | assertFailure(t.Never.decode('foo'), ['Invalid value "foo" supplied to : never']); 161 | assertFailure(t.Never.decode(1), ['Invalid value 1 supplied to : never']); 162 | assertFailure(t.Never.decode(true), ['Invalid value true supplied to : never']); 163 | assertFailure(t.Never.decode(t.identity), ['Invalid value supplied to : never']); 164 | assertFailure(t.Never.decode({}), ['Invalid value {} supplied to : never']); 165 | assertFailure(t.Never.decode([]), ['Invalid value [] supplied to : never']); 166 | assertFailure(t.Never.decode(/a/), ['Invalid value /a/ supplied to : never']); 167 | }); 168 | 169 | it('should not accept any value', () => { 170 | assert.ok(!t.Never.is(null)); 171 | assert.ok(!t.Never.is(undefined)); 172 | assert.ok(!t.Never.is('foo')); 173 | assert.ok(!t.Never.is(1)); 174 | assert.ok(!t.Never.is(true)); 175 | assert.ok(!t.Never.is(t.identity)); 176 | assert.ok(!t.Never.is({})); 177 | assert.ok(!t.Never.is([])); 178 | assert.ok(!t.Never.is(/a/)); 179 | }); 180 | 181 | it('should not encode any value', () => { 182 | // $FlowExpectError 183 | assert.throws(() => t.Never.encode('a')); 184 | }); 185 | }); 186 | 187 | describe('boolean', () => { 188 | it('should properly typecheck values', () => { 189 | const T = t.Boolean; 190 | const a: t.TypeOf = true; 191 | const b: t.OutputOf = true; 192 | // $FlowExpectError 193 | const c: t.TypeOf = 1; 194 | // $FlowExpectError 195 | const d: t.OutputOf = []; 196 | }); 197 | it('should decode boolean values', () => { 198 | assertSuccess(t.Boolean.decode(true)); 199 | assertSuccess(t.Boolean.decode(false)); 200 | }); 201 | 202 | it('should not decode non-boolean values', () => { 203 | assertFailure(t.Boolean.decode(1), ['Invalid value 1 supplied to : boolean']); 204 | }); 205 | }); 206 | -------------------------------------------------------------------------------- /test/types/props/exact.js: -------------------------------------------------------------------------------- 1 | //@flow 2 | import * as t from '../../../src/index'; 3 | import type { TypeOf } from '../../../src/index'; 4 | import { assertSuccess, assertFailure, assertStrictEqual, DateFromNumber } from '../../helpers'; 5 | import * as assert from 'assert'; 6 | 7 | describe('exact', () => { 8 | it('should properly typecheck values', () => { 9 | /** Exact **/ 10 | const required = { foo: t.String }; 11 | const optional = { bar: DateFromNumber, baz: t.Number }; 12 | const T = t.exact({ required, optional }); 13 | const a: t.TypeOf = { foo: 'hi', bar: new Date() }; 14 | const b: t.OutputOf = { foo: 'hi', bar: 1 }; 15 | // $FlowExpectError 16 | const c: t.TypeOf = { foo: 'hi', bar: new Date(), extra: 1 }; 17 | // $FlowExpectError 18 | const d: t.OutputOf = { foo: 'hi', bar: 1, extra: 1 }; 19 | // $FlowExpectError 20 | const e: t.TypeOf = { foo: 1, bar: 1 }; 21 | // $FlowExpectError 22 | const f: t.TypeOf = ({}: {||}); 23 | // $FlowExpectError 24 | const g: t.TypeOf = 1; 25 | 26 | const T2 = T.shape(); 27 | const h: t.TypeOf = { foo: 'hi' }; 28 | const i: t.OutputOf = { bar: 1 }; 29 | const j: t.TypeOf = {}; 30 | // $FlowExpectError 31 | const k: t.TypeOf = { foo: 'hi', bar: new Date(), extra: 1 }; 32 | // $FlowExpectError 33 | const l: t.OutputOf = { foo: 'hi', bar: 1, extra: 1 }; 34 | // $FlowExpectError 35 | const m: t.TypeOf = { foo: 1, bar: 1 }; 36 | // $FlowExpectError 37 | const n: t.TypeOf = 1; 38 | 39 | const T3 = T.readOnly(); 40 | const o: t.TypeOf = { foo: 'hi' }; 41 | const p: t.OutputOf = { foo: 'hi', bar: 1 }; 42 | // $FlowExpectError 43 | const q: t.TypeOf = { foo: 'hi', bar: new Date(), extra: 1 }; 44 | // $FlowExpectError 45 | const s: t.OutputOf = { foo: 'hi', bar: 1, extra: 1 }; 46 | // $FlowExpectError 47 | const u: t.TypeOf = 1; 48 | 49 | const T4 = T.inexact(); 50 | const v: t.TypeOf = { foo: 'hi' }; 51 | const w: t.OutputOf = { foo: 'hi', bar: 1 }; 52 | const x: t.TypeOf = { foo: 'hi', bar: new Date(), extra: 1 }; 53 | const y: t.OutputOf = { foo: 'hi', bar: 1, extra: 1 }; 54 | // $FlowExpectError 55 | const z: t.TypeOf = { foo: 1, bar: new Date() }; 56 | // $FlowExpectError 57 | const aa: t.TypeOf = 1; 58 | }); 59 | 60 | it('should succeed validating a valid value (required)', () => { 61 | const T = t.exactAll({ foo: t.String }); 62 | assertSuccess(T.decode({ foo: 'foo' })); 63 | }); 64 | 65 | it('should succeed validating a valid value (shape)', () => { 66 | const T = t.exactShape({ foo: t.String }); 67 | assertSuccess(T.decode({ foo: 'foo' })); 68 | // assertSuccess(T.decode({ foo: undefined })); 69 | assertSuccess(T.decode({})); 70 | }); 71 | 72 | it('should succeed validating a valid value (required + optional)', () => { 73 | const required = { foo: t.String }; 74 | const optional = { bar: t.Number }; 75 | const T = t.exact({ required, optional }); 76 | assertSuccess(T.decode({ foo: 'foo', bar: 1 })); 77 | // assertSuccess(T.decode({ foo: 'foo', bar: undefined })) 78 | assertSuccess(T.decode({ foo: 'foo' })); 79 | }); 80 | 81 | it('should succeed validating a valid value (refinement)', () => { 82 | const T = t.refinement(t.exactAll({ foo: t.String }), p => p.foo.length > 2); 83 | assertSuccess(T.decode({ foo: 'foo' })); 84 | }); 85 | 86 | it('should succeed validating a valid value (readonly)', () => { 87 | const T = t.exactAll({ foo: t.String }).readOnly(); 88 | assertSuccess(T.decode({ foo: 'foo' })); 89 | }); 90 | 91 | it('should succeed validating an undefined field', () => { 92 | const T = t.exact({ required: { foo: t.String }, optional: { bar: t.String } }); 93 | assertSuccess(T.decode({ foo: 'foo' })); 94 | }); 95 | 96 | it('should return the same reference if validation succeeded', () => { 97 | const T = t.exactAll({ foo: t.String }); 98 | const value = { foo: 'foo' }; 99 | assertStrictEqual(T.decode(value), value); 100 | }); 101 | 102 | it('should fail validating an invalid value (type)', () => { 103 | const T = t.exactAll({ foo: t.String }); 104 | assertFailure(T.decode({ foo: 'foo', bar: 1, baz: true }), [ 105 | 'Invalid value 1 supplied to : {| foo: string |}/bar: never', 106 | 'Invalid value true supplied to : {| foo: string |}/baz: never' 107 | ]); 108 | }); 109 | 110 | it('should fail validating an invalid value (partial)', () => { 111 | const T = t.exact({ required: { foo: t.String }, optional: { bar: t.Number } }); 112 | assertFailure(T.decode({ foo: 'foo', baz: true }), [ 113 | 'Invalid value true supplied to : {| foo: string, bar?: number |}/baz: never' 114 | ]); 115 | }); 116 | 117 | it('should fail validating an invalid value (shape)', () => { 118 | const T = t.exactShape({ foo: t.String }); 119 | assertFailure(T.decode({ bar: 1 }), ['Invalid value 1 supplied to : {| foo?: string |}/bar: never']); 120 | }); 121 | 122 | it('should fail validating an invalid value (refinement)', () => { 123 | const T = t.refinement(t.exactAll({ foo: t.String }), p => p.foo.length > 2); 124 | assertFailure(T.decode({ foo: 'foo', bar: 1 }), [ 125 | 'Invalid value 1 supplied to : ({| foo: string |} | )/bar: never' 126 | ]); 127 | }); 128 | 129 | it('should fail validating an invalid value (readonly)', () => { 130 | const T = t.exactAll({ foo: t.String }).readOnly(); 131 | assertFailure(T.decode({ foo: 'foo', bar: 1 }), ['Invalid value 1 supplied to : {| +foo: string |}/bar: never']); 132 | }); 133 | 134 | it('should assign a default name', () => { 135 | const T1 = t.exactAll({ foo: t.String }, 'Foo'); 136 | assert.strictEqual(T1.name, 'Foo'); 137 | const T2 = t.exactAll({ foo: t.String }); 138 | assert.strictEqual(T2.name, '{| foo: string |}'); 139 | }); 140 | 141 | it('should serialize a deserialized', () => { 142 | const T = t.exactAll({ a: DateFromNumber }); 143 | assert.deepEqual(T.encode({ a: new Date(0) }), { a: 0 }); 144 | }); 145 | 146 | it('should return the same reference when serializing', () => { 147 | const T = t.exactAll({ a: t.Number }); 148 | assert.strictEqual(T.encode, t.identity); 149 | }); 150 | 151 | it('should type guard', () => { 152 | const T1 = t.exactAll({ a: t.Number }); 153 | assert.strictEqual(T1.is({ a: 0 }), true); 154 | assert.strictEqual(T1.is({ a: 0, b: 1 }), false); 155 | assert.strictEqual(T1.is(undefined), false); 156 | const T2 = t.exactAll({ a: DateFromNumber }); 157 | assert.strictEqual(T2.is({ a: new Date(0) }), true); 158 | assert.strictEqual(T2.is({ a: new Date(0), b: 1 }), false); 159 | assert.strictEqual(T2.is(undefined), false); 160 | }); 161 | 162 | it('should be convertable to inexact type', () => { 163 | const T = t.exactAll({ a: DateFromNumber }); 164 | const x = { a: 0, b: 'foo' }; 165 | assertFailure(T.decode(x), ['Invalid value "foo" supplied to : {| a: DateFromNumber |}/b: never']); 166 | const T2 = T.inexact(); 167 | assertSuccess(T2.decode(x)); 168 | }); 169 | 170 | it('should be convertable to $Shape type', () => { 171 | const T = t.exactAll({ a: DateFromNumber }); 172 | const x = {}; 173 | assertFailure(T.decode(x), ['Invalid value undefined supplied to : {| a: DateFromNumber |}/a: DateFromNumber']); 174 | const T2 = T.shape(); 175 | assertSuccess(T2.decode(x)); 176 | assertSuccess(T2.decode({ a: 1 })); 177 | assertFailure(T2.decode({ a: 'hi' }), [ 178 | 'Invalid value "hi" supplied to : {| a?: DateFromNumber |}/a: DateFromNumber' 179 | ]); 180 | }); 181 | }); 182 | -------------------------------------------------------------------------------- /scripts/test.js: -------------------------------------------------------------------------------- 1 | //@flow 2 | /* eslint no-console: 0 */ 3 | 4 | import {child_process, fs, os, path} from 'flow-typed/dist/lib/node.js'; 5 | import {recursiveRmdir} from 'flow-typed/dist/lib/fileUtils.js'; 6 | import {gitHubClient} from 'flow-typed/dist/lib/github.js'; 7 | import got from 'got'; 8 | import * as semver from 'semver'; 9 | import * as unzip from 'unzipper'; 10 | import {peerDependencies} from '../package.json'; 11 | 12 | const PKG_ROOT_DIR = path.join(__dirname, '..'); 13 | const BIN_DIR = path.join(PKG_ROOT_DIR, 'scripts', '.flow-bins-cache'); 14 | const TEST_DIR = path.join(PKG_ROOT_DIR, 'test'); 15 | const BATCH_SIZE = parseInt(process.env.BATCH_SIZE || 5); 16 | const P = Promise; 17 | 18 | const BIN_PLATFORM = (_ => { 19 | switch (os.type()) { 20 | case 'Linux': 21 | return 'linux64'; 22 | case 'Darwin': 23 | return 'osx'; 24 | case 'Windows_NT': 25 | return 'win64'; 26 | default: 27 | throw new Error('Unsupported os.type()! ' + os.type()); 28 | } 29 | })(); 30 | 31 | getOrderedFlowBinVersions().then(runFlowTypeDefTests).then(printErrs); 32 | 33 | function printErrs(errors) { 34 | if (errors.length > 0) { 35 | console.log('ERROR:'); 36 | errors.forEach(err => 37 | console.log( 38 | '* %s\n', 39 | err.split('\n') 40 | .map((line, idx) => idx === 0 ? line : ' ' + line) 41 | .join('\n'), 42 | ) 43 | ); 44 | process.exit(1); 45 | } else { 46 | console.log('Testing complete') 47 | process.exit(0); 48 | } 49 | } 50 | 51 | async function runFlowTypeDefTests(flowVersionsToRun) { 52 | const errors = []; 53 | while (flowVersionsToRun.length > 0) { 54 | // Run tests in batches to avoid saturation 55 | const testBatch = flowVersionsToRun 56 | .slice(0, Math.min(flowVersionsToRun.length, BATCH_SIZE)) 57 | .map(group => (flowVersionsToRun.shift(), group)); 58 | 59 | await P.all( 60 | testBatch.map(async flowVer => { 61 | const testRunId = ' (flow-' + flowVer + ')'; 62 | console.log('Testing %s...', testRunId); 63 | 64 | const {stdErrOut, errCode, execError} = await testTypeDefinition(flowVer, TEST_DIR); 65 | 66 | if (execError !== null) { 67 | errors.push(testRunId + ': Error executing Flow process: ' + execError.stack); 68 | } else if (!stdErrOut.endsWith('Found 0 errors\n')) { 69 | errors.push(testRunId + ': Unexpected Flow errors (' + String(errCode) + '):\n\n' + stdErrOut); 70 | } 71 | }), 72 | ); 73 | } 74 | return errors; 75 | } 76 | 77 | function testTypeDefinition(flowVer, testDirPath) { 78 | return new Promise(res => { 79 | const child = child_process.exec( 80 | [ path.join(BIN_DIR, 'flow-' + flowVer), 'check', '--strip-root', testDirPath, ].join(' ') 81 | ); 82 | 83 | let stdErrOut = ''; 84 | child.stdout.on('data', data => (stdErrOut += data)); 85 | child.stderr.on('data', data => (stdErrOut += data)); 86 | child.on('error', execError => res({stdErrOut, errCode: null, execError})); 87 | child.on('close', errCode => res({stdErrOut, errCode, execError: null})); 88 | }); 89 | } 90 | 91 | let _flowBinVersionPromise = null; 92 | async function getOrderedFlowBinVersions(numberOfReleases: number = 20): Promise> { 93 | if (!(await fs.exists(BIN_DIR))) await fs.mkdir(BIN_DIR); 94 | if (_flowBinVersionPromise != undefined) _flowBinVersionPromise; 95 | return (_flowBinVersionPromise = (async function() { 96 | console.log('Fetching all Flow binaries...'); 97 | const IS_WINDOWS = os.type() === 'Windows_NT'; 98 | const GH_CLIENT = gitHubClient(); 99 | // We only test against the latest numberOfReleases Versions 100 | const QUERY_PAGE_SIZE = numberOfReleases; 101 | const OS_ARCH_FILTER_RE = new RegExp(`flow-${BIN_PLATFORM}`); 102 | 103 | let page = 0; 104 | const apiPayload = await GH_CLIENT.repos.listReleases({ 105 | owner: 'facebook', 106 | repo: 'flow', 107 | page: page++, 108 | per_page: QUERY_PAGE_SIZE, 109 | }); 110 | 111 | const flowBins = apiPayload.data 112 | .filter(rel => { 113 | if (rel.tag_name === 'v0.95.0') { 114 | printSkipMessage(rel.tag_name, 'JSON.stringify issue present that was later patched'); 115 | return false; 116 | } 117 | return semver.satisfies(rel.tag_name, peerDependencies['flow-bin']); 118 | }) 119 | .map(rel => { 120 | // Find the binary zip in the list of assets 121 | const binZip = rel.assets 122 | .filter(({name}) => OS_ARCH_FILTER_RE.test(name) && !/-latest.zip$/.test(name)) 123 | .map(asset => asset.browser_download_url); 124 | 125 | if (binZip.length !== 1) { 126 | throw new Error( 127 | 'Unexpected number of ' + BIN_PLATFORM + 128 | ' assets for flow-' + rel.tag_name + '! ' + JSON.stringify(binZip), 129 | ); 130 | } else { 131 | const version = rel.tag_name[0] === 'v' ? rel.tag_name : 'v' + rel.tag_name; 132 | return {version, binURL: binZip[0]}; 133 | } 134 | }) 135 | .sort((a, b) => semver.lt(a.version, b.version) ? -1 : 1); 136 | 137 | await Promise.all( 138 | flowBins.map(async ({version, binURL}) => { 139 | const zipPath = path.join(BIN_DIR, 'flow-' + version + '.zip'); 140 | const binPath = path.join(BIN_DIR, 'flow-' + version + (IS_WINDOWS ? '.exe' : '')); 141 | if (await fs.exists(binPath)) return; 142 | console.log("PATHS"); 143 | console.log(zipPath); 144 | console.log(binPath); 145 | 146 | // Download the zip file 147 | await new Promise((res, rej) => { 148 | console.log(' Fetching flow-%s...', version); 149 | got 150 | .stream(binURL, { 151 | headers: { 152 | 'User-Agent': 153 | 'flow-typed Test Runner ' + 154 | '(github.com/flowtype/flow-typed)', 155 | }, 156 | }) 157 | .on('error', err => rej(err)) 158 | .pipe( 159 | fs.createWriteStream(zipPath).on('close', () => { 160 | console.log(' flow-%s finished downloading.', version); 161 | res(); 162 | }), 163 | ); 164 | }); 165 | 166 | // Extract the flow binary 167 | const flowBinDirPath = path.join(BIN_DIR, 'TMP-flow-' + version); 168 | await fs.mkdir(flowBinDirPath); 169 | console.log(' Extracting flow-%s...', version); 170 | await new Promise((res, rej) => { 171 | const unzipExtractor = unzip.Extract({path: flowBinDirPath}); 172 | unzipExtractor.on('error', err => rej(err)); 173 | unzipExtractor.on('close', () => res()); 174 | fs.createReadStream(zipPath).pipe(unzipExtractor); 175 | }); 176 | if (IS_WINDOWS) { 177 | await fs.rename( 178 | path.join(flowBinDirPath, 'flow', 'flow.exe'), 179 | path.join(BIN_DIR, 'flow-' + version + '.exe'), 180 | ); 181 | } else { 182 | await fs.rename( 183 | path.join(flowBinDirPath, 'flow', 'flow'), 184 | path.join(BIN_DIR, 'flow-' + version), 185 | ); 186 | 187 | await child_process.execP(['chmod', '755', path.join(BIN_DIR, 'flow-' + version)].join(' ')); 188 | } 189 | 190 | console.log(' Removing flow-%s artifacts...', version); 191 | await P.all([recursiveRmdir(flowBinDirPath), fs.unlink(zipPath)]); 192 | console.log(' flow-%s complete!', version); 193 | }), 194 | ); 195 | 196 | console.log('Finished fetching Flow binaries.\n'); 197 | 198 | return flowBins.map(bin => bin.version); 199 | })()); 200 | } 201 | 202 | function printSkipMessage(flowVersion, githubUrl) { 203 | console.log( 204 | '==========================================================================================', 205 | ); 206 | console.log(`We are temporarily skipping ${flowVersion} due to ${githubUrl}`); 207 | console.log( 208 | '==========================================================================================', 209 | ); 210 | } 211 | -------------------------------------------------------------------------------- /flow-typed/npm/bluebird_v3.x.x.js: -------------------------------------------------------------------------------- 1 | // flow-typed signature: e0974d6c2c04803a78c4faa3e848c72a 2 | // flow-typed version: 34fbdaa6f3/bluebird_v3.x.x/flow_>=v0.70.x 3 | 4 | type Bluebird$RangeError = Error; 5 | type Bluebird$CancellationErrors = Error; 6 | type Bluebird$TimeoutError = Error; 7 | type Bluebird$RejectionError = Error; 8 | type Bluebird$OperationalError = Error; 9 | 10 | type Bluebird$ConcurrencyOption = { 11 | concurrency: number 12 | }; 13 | type Bluebird$SpreadOption = { 14 | spread: boolean 15 | }; 16 | type Bluebird$MultiArgsOption = { 17 | multiArgs: boolean 18 | }; 19 | type Bluebird$BluebirdConfig = { 20 | warnings?: boolean, 21 | longStackTraces?: boolean, 22 | cancellation?: boolean, 23 | monitoring?: boolean 24 | }; 25 | 26 | declare class Bluebird$PromiseInspection { 27 | isCancelled(): boolean; 28 | isFulfilled(): boolean; 29 | isRejected(): boolean; 30 | pending(): boolean; 31 | reason(): any; 32 | value(): T; 33 | } 34 | 35 | type Bluebird$PromisifyOptions = {| 36 | multiArgs?: boolean, 37 | context: any 38 | |}; 39 | 40 | declare type Bluebird$PromisifyAllOptions = { 41 | suffix?: string, 42 | filter?: ( 43 | name: string, 44 | func: Function, 45 | target?: any, 46 | passesDefaultFilter?: boolean 47 | ) => boolean, 48 | // The promisifier gets a reference to the original method and should return a function which returns a promise 49 | promisifier?: (originalMethod: Function) => () => Bluebird$Promise 50 | }; 51 | 52 | declare type $Promisable = Promise | T; 53 | 54 | declare class Bluebird$Disposable {} 55 | 56 | declare class Bluebird$Promise<+R> extends Promise { 57 | static RangeError: Class; 58 | static CancellationErrors: Class; 59 | static TimeoutError: Class; 60 | static RejectionError: Class; 61 | static OperationalError: Class; 62 | 63 | static Defer: Class; 64 | static PromiseInspection: Class>; 65 | 66 | static all( 67 | Promises: $Promisable>> 68 | ): Bluebird$Promise>; 69 | static props( 70 | input: Object | Map<*, *> | $Promisable> 71 | ): Bluebird$Promise<*>; 72 | static any>( 73 | Promises: Iterable | $Promisable> 74 | ): Bluebird$Promise; 75 | static race>( 76 | Promises: Iterable | $Promisable> 77 | ): Bluebird$Promise; 78 | static reject(error?: any): Bluebird$Promise; 79 | static resolve(object?: $Promisable): Bluebird$Promise; 80 | static some>( 81 | Promises: Iterable | $Promisable>, 82 | count: number 83 | ): Bluebird$Promise>; 84 | static join( 85 | value1: $Promisable, 86 | handler: (a: A) => $Promisable 87 | ): Bluebird$Promise; 88 | static join( 89 | value1: $Promisable, 90 | value2: $Promisable, 91 | handler: (a: A, b: B) => $Promisable 92 | ): Bluebird$Promise; 93 | static join( 94 | value1: $Promisable, 95 | value2: $Promisable, 96 | value3: $Promisable, 97 | handler: (a: A, b: B, c: C) => $Promisable 98 | ): Bluebird$Promise; 99 | static map>( 100 | Promises: Iterable | $Promisable>, 101 | mapper: (item: T, index: number, arrayLength: number) => $Promisable, 102 | options?: Bluebird$ConcurrencyOption 103 | ): Bluebird$Promise>; 104 | static mapSeries>( 105 | Promises: Iterable | $Promisable>, 106 | mapper: (item: T, index: number, arrayLength: number) => $Promisable 107 | ): Bluebird$Promise>; 108 | static reduce>( 109 | Promises: Iterable | $Promisable>, 110 | reducer: ( 111 | total: U, 112 | current: T, 113 | index: number, 114 | arrayLength: number 115 | ) => $Promisable, 116 | initialValue?: $Promisable 117 | ): Bluebird$Promise; 118 | static filter>( 119 | Promises: Iterable | $Promisable>, 120 | filterer: ( 121 | item: T, 122 | index: number, 123 | arrayLength: number 124 | ) => $Promisable, 125 | option?: Bluebird$ConcurrencyOption 126 | ): Bluebird$Promise>; 127 | static each>( 128 | Promises: Iterable | $Promisable>, 129 | iterator: ( 130 | item: T, 131 | index: number, 132 | arrayLength: number 133 | ) => $Promisable 134 | ): Bluebird$Promise>; 135 | static try( 136 | fn: () => $Promisable, 137 | args: ?Array, 138 | ctx: ?any 139 | ): Bluebird$Promise; 140 | static attempt( 141 | fn: () => $Promisable, 142 | args: ?Array, 143 | ctx: ?any 144 | ): Bluebird$Promise; 145 | static delay(ms: number, value: $Promisable): Bluebird$Promise; 146 | static delay(ms: number): Bluebird$Promise; 147 | static config(config: Bluebird$BluebirdConfig): void; 148 | 149 | static defer(): Bluebird$Defer; 150 | static setScheduler( 151 | scheduler: (callback: (...args: Array) => void) => void 152 | ): void; 153 | static promisify( 154 | nodeFunction: Function, 155 | receiver?: Bluebird$PromisifyOptions 156 | ): Function; 157 | static promisifyAll( 158 | target: Object | Array, 159 | options?: Bluebird$PromisifyAllOptions 160 | ): void; 161 | 162 | static coroutine(generatorFunction: Function): Function; 163 | static spawn(generatorFunction: Function): Promise; 164 | 165 | // It doesn't seem possible to have type-generics for a variable number of arguments. 166 | // Handle up to 3 arguments, then just give up and accept 'any'. 167 | static method>(fn: () => R): () => Bluebird$Promise; 168 | static method, A>( 169 | fn: (a: A) => R 170 | ): (a: A) => Bluebird$Promise; 171 | static method, A, B>( 172 | fn: (a: A, b: B) => R 173 | ): (a: A, b: B) => Bluebird$Promise; 174 | static method, A, B, C>( 175 | fn: (a: A, b: B, c: C) => R 176 | ): (a: A, b: B, c: C) => Bluebird$Promise; 177 | static method>( 178 | fn: (...args: any) => R 179 | ): (...args: any) => Bluebird$Promise; 180 | 181 | static cast(value: $Promisable): Bluebird$Promise; 182 | // static bind(ctx: any): Bluebird$Promise; 183 | static is(value: any): boolean; 184 | static longStackTraces(): void; 185 | 186 | static onPossiblyUnhandledRejection(handler: (reason: any) => any): void; 187 | static fromCallback( 188 | resolver: (fn: (error: ?Error, value?: T) => any) => any, 189 | options?: Bluebird$MultiArgsOption 190 | ): Bluebird$Promise; 191 | 192 | constructor( 193 | callback: ( 194 | resolve: (result?: $Promisable) => void, 195 | reject: (error?: any) => void, 196 | onCancel?: (fn?: () => void) => void, 197 | ) => mixed 198 | ): void; 199 | then(onFulfill: null | void, onReject: null | void): Bluebird$Promise; 200 | then( 201 | onFulfill: null | void, 202 | onReject: (error: any) => Promise | U 203 | ): Bluebird$Promise; 204 | then( 205 | onFulfill: (value: R) => Promise | U, 206 | onReject: null | void | ((error: any) => Promise | U) 207 | ): Bluebird$Promise; 208 | catch(onReject: null | void): Promise; 209 | catch(onReject?: (error: any) => $Promisable): Bluebird$Promise; 210 | catch( 211 | err: Class, 212 | onReject: (error: ErrorT) => $Promisable 213 | ): Bluebird$Promise; 214 | catch( 215 | err1: Class, 216 | err2: Class, 217 | onReject: (error: ErrorT) => $Promisable 218 | ): Bluebird$Promise; 219 | catch( 220 | err1: Class, 221 | err2: Class, 222 | err3: Class, 223 | onReject: (error: ErrorT) => $Promisable 224 | ): Bluebird$Promise; 225 | caught( 226 | err: Class, 227 | onReject: (error: Error) => $Promisable 228 | ): Bluebird$Promise; 229 | caught( 230 | err1: Class, 231 | err2: Class, 232 | onReject: (error: ErrorT) => $Promisable 233 | ): Bluebird$Promise; 234 | caught( 235 | err1: Class, 236 | err2: Class, 237 | err3: Class, 238 | onReject: (error: ErrorT) => $Promisable 239 | ): Bluebird$Promise; 240 | caught(onReject: (error: any) => $Promisable): Bluebird$Promise; 241 | 242 | error(onReject?: (error: any) => ?$Promisable): Bluebird$Promise; 243 | done( 244 | onFulfill?: (value: R) => mixed, 245 | onReject?: (error: any) => mixed 246 | ): void; 247 | finally(onDone?: (value: R) => mixed): Bluebird$Promise; 248 | lastly(onDone?: (value: R) => mixed): Bluebird$Promise; 249 | tap(onDone?: (value: R) => mixed): Bluebird$Promise; 250 | delay(ms: number): Bluebird$Promise; 251 | timeout(ms: number, message?: string): Bluebird$Promise; 252 | cancel(): void; 253 | 254 | // bind(ctx: any): Bluebird$Promise; 255 | call(propertyName: string, ...args: Array): Bluebird$Promise; 256 | throw(reason: Error): Bluebird$Promise; 257 | thenThrow(reason: Error): Bluebird$Promise; 258 | all(): Bluebird$Promise>; 259 | any(): Bluebird$Promise; 260 | some(count: number): Bluebird$Promise>; 261 | race(): Bluebird$Promise; 262 | map( 263 | mapper: (item: T, index: number, arrayLength: number) => $Promisable, 264 | options?: Bluebird$ConcurrencyOption 265 | ): Bluebird$Promise>; 266 | mapSeries( 267 | mapper: (item: T, index: number, arrayLength: number) => $Promisable 268 | ): Bluebird$Promise>; 269 | reduce( 270 | reducer: ( 271 | total: T, 272 | item: U, 273 | index: number, 274 | arrayLength: number 275 | ) => $Promisable, 276 | initialValue?: $Promisable 277 | ): Bluebird$Promise; 278 | filter( 279 | filterer: ( 280 | item: T, 281 | index: number, 282 | arrayLength: number 283 | ) => $Promisable, 284 | options?: Bluebird$ConcurrencyOption 285 | ): Bluebird$Promise>; 286 | each( 287 | iterator: (item: T, index: number, arrayLength: number) => $Promisable 288 | ): Bluebird$Promise>; 289 | asCallback( 290 | callback: (error: ?any, value?: T) => any, 291 | options?: Bluebird$SpreadOption 292 | ): void; 293 | return(value: T): Bluebird$Promise; 294 | thenReturn(value: T): Bluebird$Promise; 295 | spread(...args: Array): Bluebird$Promise<*>; 296 | 297 | reflect(): Bluebird$Promise>; 298 | 299 | isFulfilled(): boolean; 300 | isRejected(): boolean; 301 | isPending(): boolean; 302 | isResolved(): boolean; 303 | 304 | value(): R; 305 | reason(): any; 306 | 307 | disposer( 308 | disposer: (value: R, promise: Promise<*>) => void 309 | ): Bluebird$Disposable; 310 | 311 | static using( 312 | disposable: Bluebird$Disposable, 313 | handler: (value: T) => $Promisable 314 | ): Bluebird$Promise; 315 | 316 | suppressUnhandledRejections(): void; 317 | } 318 | 319 | declare class Bluebird$Defer { 320 | promise: Bluebird$Promise<*>; 321 | resolve: (value: any) => any; 322 | reject: (value: any) => any; 323 | } 324 | 325 | declare module "bluebird" { 326 | declare module.exports: typeof Bluebird$Promise; 327 | 328 | declare type Disposable = Bluebird$Disposable; 329 | } 330 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | [![CircleCI](https://circleci.com/gh/orlandoc01/io-flow-types/tree/master.svg?style=svg)](https://circleci.com/gh/orlandoc01/io-flow-types/tree/master) 2 | ![Dependencies](https://david-dm.org/orlandoc01/io-flow-types.svg) 3 | [![Minzip Size](https://badgen.net/bundlephobia/minzip/io-flow-types)](https://bundlephobia.com/result?p=io-flow-types) 4 | 5 | # Summary 6 | The work that gcanti has done with [io-ts](https://github.com/gcanti/io-ts) is really useful, but it relies on a lot of language semantics that are exclusive to Typescript and the work on [flow-io](https://github.com/gcanti/flow-io) is now deprecated and no longer maintained. As a result, I have forked his work and refactored a lot of the existing combinators/classes so they more easily align with the semantics of [Flow](https://flow.org/). 7 | 8 | # Table of Contents 9 | - [The idea](#the-idea) 10 | - [Error handling](#error-handling) 11 | - [Error reporters](#error-reporters) 12 | - [Implemented types / combinators](#implemented-types--combinators) 13 | - [Flow compatibility](#flow-compatibility) 14 | - [Flow integration](#flow-integration) 15 | - [Mixing required and optional props](#mixing-required-and-optional-props) 16 | - [Union Maps](#union-maps) 17 | - [Refinements](#refinements) 18 | - [Custom types](#custom-types) 19 | - [Custom Error Messages](#custom-error-messages) 20 | - [Tips and Tricks](#tips-and-tricks) 21 | - [Is there a way to turn the checks off in production code?](#is-there-a-way-to-turn-the-checks-off-in-production-code) 22 | - [Union of string literals](#union-of-string-literals) 23 | 24 | # The idea 25 | 26 | Blog post: ["Typescript and validations at runtime boundaries"](https://lorefnon.tech/2018/03/25/typescript-and-validations-at-runtime-boundaries/) by [@lorefnon](https://github.com/lorefnon) 27 | 28 | Similiar to [io-ts](https://github.com/gcanti/io-ts), a value of type `Type` (called "runtime type") is the runtime representation of the static type `A`. 29 | 30 | A runtime type can 31 | 32 | - decode inputs of type `I` (through `decode`) 33 | - encode outputs of type `O` (through `encode`) 34 | - be used as a custom type guard (through `is`) 35 | 36 | ```javascript 37 | type Validation = Either; 38 | 39 | class Type { 40 | +_A: A 41 | +_O: O 42 | +_I: I 43 | constructor( 44 | /** a unique name for this runtime type */ 45 | name: string, 46 | /** a custom type guard */ 47 | is: (v: mixed) => boolean, 48 | /** succeeds if a value of type I can be decoded to a value of type A */ 49 | validate: (input: I, context: Context) => Validation, 50 | /** converts a value of type A to a value of type O */ 51 | encode: (a: A) => O 52 | ) {} 53 | /** a version of `validate` with a default context */ 54 | decode(i: I): Either 55 | /** a version of `validate` which will throw if invalid */ 56 | assert(i: I): A 57 | } 58 | ``` 59 | 60 | **Example** 61 | 62 | A runtime type representing `string` can be defined as 63 | 64 | ```javascript 65 | import * as t from 'io-flow-types' 66 | 67 | // runtime type definition 68 | export class StringType extends t.Type { 69 | // equivalent to Type as per type parameter defaults 70 | constructor() { 71 | super( 72 | 'string', 73 | (m): m is string => typeof m === 'string', 74 | (m, c) => (this.is(m) ? t.success(m) : t.failure(m, c)), 75 | t.identity 76 | ) 77 | } 78 | } 79 | 80 | // runtime type instance: use this when building other runtime types instances 81 | export const string = new StringType() 82 | ``` 83 | 84 | A runtime type can be used to validate an object in memory (for example an API payload) 85 | 86 | ```javascript 87 | const Person = t.inexactAll({name: t.String, age: t.Number}) 88 | 89 | // validation succeeded 90 | Person.decode(JSON.parse('{"name":"John","age":43}')) // => right({name: "John", age: 43}) 91 | 92 | // validation failed 93 | Person.decode(JSON.parse('{"name":"John"}')) // => left([...]) 94 | 95 | //assertion succeeded 96 | Person.assert(JSON.parse('{"name":"John","age":43}')) // => {name: "John", age: 43} 97 | 98 | //async assertion succeeded 99 | Promise.resolve('{"name":"John","age":43}') 100 | .then(JSON.parse) 101 | .then(Person.getAssert()) 102 | .then(val => { 103 | return val // => {name: "John", age: 43}) 104 | }); 105 | ``` 106 | # Error handling 107 | 108 | An error that is uncovered during decoding will be packed into an instance of the `ValidationError` class. 109 | ```javascript 110 | class ValidationError extends Error { 111 | +value: mixed; 112 | +context: Context; 113 | +message: string; 114 | constructor(value: mixed, context: Context, message?: string) 115 | } 116 | ``` 117 | 118 | Besides having a `message` property, as is standard for Error classes in JavaScript, it also references the value which failed validation along with the context that was used in decoding. By default, if a message isn't supplied, a default one will be constructed based on the context reference 119 | 120 | All errors that are uncovered during decoding will be packed into an instance of the `AggregateErrors` class, which is a subclass of `Array`. 121 | ```javascript 122 | class AggregateError extends Array { 123 | constructor(...args: ValidationError[]) 124 | messages(): Array 125 | } 126 | ``` 127 | Errors can be still be extracted individually as elements of the wrapped array, and the messages can be extracted all at once via the introduction of the `messages()` method on this class. 128 | 129 | An example of Error inspection is shown below: 130 | ```javascript 131 | const Person = t.inexactAll({name: t.String, age: t.Number}) 132 | 133 | // validation failed with decode 134 | const leftErr = Person.decode(JSON.parse('{}')) // => left([...]) 135 | 136 | if (leftErr.tag === 'Left') { 137 | const errs = leftErr.value; 138 | console.log(errs[0].message) 139 | // => Invalid value undefined supplied to : { name: string, age: number }/name: string 140 | console.log(errs[1].message) 141 | // => Invalid value undefined supplied to : { name: string, age: number }/age: number 142 | } 143 | 144 | try { 145 | // validation throws with assert 146 | Person.assert(JSON.parse('{}')) // => left([...]) 147 | } catch (errs) { 148 | console.log(errs[0].message) 149 | // => Invalid value undefined supplied to : { name: string, age: number }/name: string 150 | console.log(errs[1].message) 151 | // => Invalid value undefined supplied to : { name: string, age: number }/age: number 152 | } 153 | 154 | ////async validation 155 | Promise.resolve('{}') 156 | .then(JSON.parse) 157 | .then(Person.getAssert()) 158 | .catch(errs => { 159 | console.log(errs[0].message) 160 | // => Invalid value undefined supplied to : { name: string, age: number }/name: string 161 | console.log(errs[1].message) 162 | // => Invalid value undefined supplied to : { name: string, age: number }/age: number 163 | }); 164 | ``` 165 | ### Error reporters 166 | 167 | To implement custom error reporters, it's recommended to design an object which can implement the following interface. 168 | 169 | ```javascript 170 | interface Reporter { 171 | report: (validation: Validation) => A 172 | } 173 | ``` 174 | 175 | The `report` method above should be prepared to take a `Validation` instance, which will be either `left(AggregateErrors)` or `right(T)` where `T` is the correctly decoded type. The reporter should check for and then use an instance of the `AggregateErrors` class and perform the necessary transformation, using the `context` and `value` properties on `ValidationError` instances packed inside `AggregateErrors` instance. 176 | 177 | The example below implements a `getPaths` function which can be used as the `report` method on a Reporter-like object. It basically will take validations that fail and print the paths that failed. 178 | 179 | ```javascript 180 | import * as t from 'io-flow-types' 181 | 182 | const getPathFromError = (error: t.ValidationError) => { 183 | return error.context.map(({ key }) => key).join('.'); 184 | } 185 | 186 | const getPaths = (v: t.Validation): Array => { 187 | return v.fold(errors => [...errors].map(getPathFromError), () => ['no errors']) 188 | } 189 | 190 | const Person = t.exactAll({name: t.String, age: t.Number }) 191 | 192 | console.log(getPaths(Person.decode({}))) // => [ '.name', '.age' ] 193 | ``` 194 | 195 | # Implemented types / combinators 196 | 197 | ```javascript 198 | import * as t from 'io-flow-types' 199 | ``` 200 | 201 | | Type | Flow | Runtime type / combinator | 202 | | ------------------------- | --------------------------------------- | ----------------------------------------------------- | 203 | | null | `null` | `t.Null` | 204 | | undefined | `undefined` | `t.Undefined` | 205 | | void | `void` | `t.Void` | 206 | | string | `string` | `t.String` | 207 | | number | `number` | `t.Number` | 208 | | boolean | `boolean` | `t.Boolean` | 209 | | any | `any` | `t.Any` | 210 | | never | `never` | `t.Never` | 211 | | object | `object` | `t.object` | 212 | | integer | ✘ | `t.Integer` | 213 | | literal | `'s'` | `t.literal<'s'>('s')` | 214 | | array of any | `Array` | `t.arrayType` | 215 | | array of type | `Array` | `t.array(A)` | 216 | | readonly array | `$ReadOnlyArray` | `t.readonlyArray(A)` | 217 | | dictionary of any | `{ [key: string]: mixed }` | `t.Dictionary` | 218 | | dictionary of type | `{ [key: A]: B }` | `t.dictionary(A, B)` | 219 | | tuple | `[ A, B ]` | `t.tuple([ A, B ])` | 220 | | union | `A \| B` | `t.union([ A, B ])` or
`t.unionMap({tagVal1: A, tagVal2: B}, tagName)` | 221 | | intersection | `A & B` | `t.intersection([ A, B ])` | 222 | | keyof | `keyof M` | `t.keyof(M)` | 223 | | refinement | `A`, `Opaque: A` | `t.refinement(A, predicate)` or
`t.opaqueRefine(A, predicate)`| 224 | | exact types | `{\| a: A, b?: B \|}` | `t.exact({required: {a :A}, optional: {b: B}})` | 225 | | | `{\| a: A, b: B \|}` | `t.exactAll({a: A, b: B})` | 226 | | | `{\| a?: A, b?: B \|}` | `t.exactShape({a: A, b: B}` | 227 | | inexact types | `{ a: A, b: b }` | `t.inexact({required: {a: A}, optional: {b: B}})` | 228 | | | `{ a: A, b: B }` | `t.inexactAll({a: A, b: B})` | 229 | | | `{ a?: A, b?: B }` | `t.inexactShape({a: A, b: B})` | 230 | 231 | *Note*: Assume `A` and `B` are instances of the `t.Type` class 232 | 233 | 234 | # Flow compatibility 235 | 236 | The library is tested against a range of `flow-bin` versions, which is listed as the `peerDependencies` section of this NPM package. 237 | 238 | # Flow integration 239 | 240 | Runtime types can be inspected 241 | 242 | ![instrospection](docs/images/introspection.png) 243 | 244 | This library uses FLow extensively. Its API is defined in a way which automatically infers types for produced values 245 | 246 | ![inference](docs/images/inference.png) 247 | 248 | Static types can be extracted from runtime types using the `TypeOf` operator 249 | 250 | ```javascript 251 | type IPerson = t.TypeOf; 252 | 253 | // same as 254 | type IPerson = { 255 | name: string 256 | age: number 257 | }; 258 | 259 | // also the same as 260 | type IPerson = $PropertyType; 261 | ``` 262 | 263 | # Mixing required and optional props 264 | 265 | You can mix required and optional props using an intersection 266 | 267 | ```javascript 268 | const required = {foo: t.string}; 269 | const optional = { bar: t.number } 270 | const C = t.exact({required, optional}) 271 | type CT = t.TypeOf; 272 | 273 | // same as 274 | type CT = { 275 | foo: string 276 | bar?: number 277 | } 278 | ``` 279 | 280 | You can call `shape` to an already defined runtime type if created with one of the `exact` or `inexact` functions 281 | 282 | ```javascript 283 | const PersonType = t.exactAll({ 284 | name: t.string, 285 | age: t.number 286 | }) 287 | 288 | const PartialPersonType = Person.shape(); 289 | 290 | type PartialPerson = t.TypeOf; 291 | 292 | // same as 293 | type PartialPerson = { 294 | name?: string 295 | age?: number 296 | } 297 | ``` 298 | 299 | # Union Maps 300 | 301 | If you are encoding tagged unions, instead of the general purpose `union` combinator, you may want to use the 302 | `unionMap` combinator in order to get better performances 303 | 304 | ```javascript 305 | const A = t.exactAll({ 306 | tag: t.literal('A'), 307 | foo: t.string 308 | }) 309 | 310 | const B = t.exactAll({ 311 | tag: t.literal('B'), 312 | bar: t.number 313 | }) 314 | 315 | const U = t.unionMap({A, B}, 'tag') 316 | ``` 317 | 318 | # Refinements 319 | 320 | You can refine a type (_any_ type) using the `refinement` combinator 321 | 322 | ```javascript 323 | const Adult = t.refinement(Person, person => person.age >= 18, 'Adult') 324 | ``` 325 | 326 | However, unless you utilize Flow's opaque types, this can't be enforced via a static check. For stricter safety, you should use the `opaqueRefine` function and supply the opaque type as a generic 327 | 328 | ```javascript 329 | opaque type Positive: number = number; 330 | const positive = t.opaqueRefine(t.Number, num => num > 0, 'Positive') 331 | ``` 332 | 333 | # Custom types 334 | 335 | You can define your own types. Let's see an example 336 | 337 | ```javascript 338 | import * as t from 'io-flow-types' 339 | 340 | // represents a Date from an ISO string 341 | const DateFromString = new t.Type( 342 | 'DateFromString', 343 | (m): m is Date => m instanceof Date, 344 | (m, c) => 345 | t.string.validate(m, c).chain(s => { 346 | const d = new Date(s) 347 | return isNaN(d.getTime()) ? t.failure(s, c) : t.success(d) 348 | }), 349 | a => a.toISOString() 350 | ) 351 | 352 | const s = new Date(1973, 10, 30).toISOString() 353 | 354 | DateFromString.decode(s) 355 | // right(new Date('1973-11-29T23:00:00.000Z')) 356 | 357 | DateFromString.decode('foo') 358 | // left(errors...) 359 | ``` 360 | 361 | Note that you can **deserialize** while validating. 362 | 363 | ### Custom Error Messages 364 | 365 | You can set your own error message by providing a `message` argument to `failure` 366 | 367 | Example 368 | 369 | ```ts 370 | const NumberFromString = new t.Type( 371 | 'NumberFromString', 372 | t.number.is, 373 | (u, c) => 374 | t.string.validate(u, c).chain(s => { 375 | const n = +s 376 | return isNaN(n) ? t.failure(u, c, 'cannot parse to a number') : t.success(n) 377 | }), 378 | String 379 | ) 380 | 381 | console.log(PathReporter.report(NumberFromString.decode('a'))) 382 | // => ['cannot parse to a number'] 383 | ``` 384 | 385 | # Tips and Tricks 386 | 387 | ## Is there a way to turn the checks off in production code? 388 | 389 | No, however you can define your own logic for that (if you _really_ trust the input) 390 | 391 | ```javascript 392 | import * as t from 'io-flow-types'; 393 | 394 | const { NODE_ENV } = process.env 395 | 396 | export function unsafeDecode(value: mixed, type: t.Type): t.Either { 397 | if (NODE_ENV !== 'production' || type.encode !== t.identity) { 398 | return type.decode(value) 399 | } else { 400 | // unsafe cast 401 | return t.Right((value: any): A) 402 | } 403 | } 404 | 405 | // or... 406 | 407 | import { failure } from 'io-flow-types/lib/PathReporter' 408 | 409 | export function unsafeGet(value: mixed, type: t.Type): A { 410 | if (NODE_ENV !== 'production' || type.encode !== t.identity) { 411 | return type.decode(value).getOrElseL(errors => { 412 | throw new Error(failure(errors).join('\n')) 413 | }) 414 | } else { 415 | // unsafe cast 416 | return ((value: any): A) 417 | } 418 | } 419 | ``` 420 | 421 | ## Union of string literals 422 | 423 | Use `keyof` instead of `union` when defining a union of string literals 424 | 425 | ```javascript 426 | const Bad = t.union([ 427 | t.literal<'foo'>('foo'), 428 | t.literal<'bar'>('bar'), 429 | t.literal<'baz'>('baz') 430 | // etc... 431 | ]) 432 | 433 | const Good = t.keyof({ 434 | foo: null, 435 | bar: null, 436 | baz: null 437 | // etc... 438 | }) 439 | ``` 440 | 441 | Benefits 442 | 443 | - unique check for free 444 | - better performance 445 | - quick info stays responsive 446 | 447 | # Known issues 448 | 449 | - TODO 450 | -------------------------------------------------------------------------------- /flow-typed/npm/jest_v22.x.x.js: -------------------------------------------------------------------------------- 1 | // flow-typed signature: 27b6ff5cf910473843da0caf82e362fe 2 | // flow-typed version: a3709d51ed/jest_v22.x.x/flow_>=v0.39.x 3 | 4 | type JestMockFn, TReturn> = { 5 | (...args: TArguments): TReturn, 6 | /** 7 | * An object for introspecting mock calls 8 | */ 9 | mock: { 10 | /** 11 | * An array that represents all calls that have been made into this mock 12 | * function. Each call is represented by an array of arguments that were 13 | * passed during the call. 14 | */ 15 | calls: Array, 16 | /** 17 | * An array that contains all the object instances that have been 18 | * instantiated from this mock function. 19 | */ 20 | instances: Array 21 | }, 22 | /** 23 | * Resets all information stored in the mockFn.mock.calls and 24 | * mockFn.mock.instances arrays. Often this is useful when you want to clean 25 | * up a mock's usage data between two assertions. 26 | */ 27 | mockClear(): void, 28 | /** 29 | * Resets all information stored in the mock. This is useful when you want to 30 | * completely restore a mock back to its initial state. 31 | */ 32 | mockReset(): void, 33 | /** 34 | * Removes the mock and restores the initial implementation. This is useful 35 | * when you want to mock functions in certain test cases and restore the 36 | * original implementation in others. Beware that mockFn.mockRestore only 37 | * works when mock was created with jest.spyOn. Thus you have to take care of 38 | * restoration yourself when manually assigning jest.fn(). 39 | */ 40 | mockRestore(): void, 41 | /** 42 | * Accepts a function that should be used as the implementation of the mock. 43 | * The mock itself will still record all calls that go into and instances 44 | * that come from itself -- the only difference is that the implementation 45 | * will also be executed when the mock is called. 46 | */ 47 | mockImplementation( 48 | fn: (...args: TArguments) => TReturn 49 | ): JestMockFn, 50 | /** 51 | * Accepts a function that will be used as an implementation of the mock for 52 | * one call to the mocked function. Can be chained so that multiple function 53 | * calls produce different results. 54 | */ 55 | mockImplementationOnce( 56 | fn: (...args: TArguments) => TReturn 57 | ): JestMockFn, 58 | /** 59 | * Accepts a string to use in test result output in place of "jest.fn()" to 60 | * indicate which mock function is being referenced. 61 | */ 62 | mockName(name: string): JestMockFn, 63 | /** 64 | * Just a simple sugar function for returning `this` 65 | */ 66 | mockReturnThis(): void, 67 | /** 68 | * Deprecated: use jest.fn(() => value) instead 69 | */ 70 | mockReturnValue(value: TReturn): JestMockFn, 71 | /** 72 | * Sugar for only returning a value once inside your mock 73 | */ 74 | mockReturnValueOnce(value: TReturn): JestMockFn 75 | }; 76 | 77 | type JestAsymmetricEqualityType = { 78 | /** 79 | * A custom Jasmine equality tester 80 | */ 81 | asymmetricMatch(value: mixed): boolean 82 | }; 83 | 84 | type JestCallsType = { 85 | allArgs(): mixed, 86 | all(): mixed, 87 | any(): boolean, 88 | count(): number, 89 | first(): mixed, 90 | mostRecent(): mixed, 91 | reset(): void 92 | }; 93 | 94 | type JestClockType = { 95 | install(): void, 96 | mockDate(date: Date): void, 97 | tick(milliseconds?: number): void, 98 | uninstall(): void 99 | }; 100 | 101 | type JestMatcherResult = { 102 | message?: string | (() => string), 103 | pass: boolean 104 | }; 105 | 106 | type JestMatcher = (actual: any, expected: any) => JestMatcherResult; 107 | 108 | type JestPromiseType = { 109 | /** 110 | * Use rejects to unwrap the reason of a rejected promise so any other 111 | * matcher can be chained. If the promise is fulfilled the assertion fails. 112 | */ 113 | rejects: JestExpectType, 114 | /** 115 | * Use resolves to unwrap the value of a fulfilled promise so any other 116 | * matcher can be chained. If the promise is rejected the assertion fails. 117 | */ 118 | resolves: JestExpectType 119 | }; 120 | 121 | /** 122 | * Jest allows functions and classes to be used as test names in test() and 123 | * describe() 124 | */ 125 | type JestTestName = string | Function; 126 | 127 | /** 128 | * Plugin: jest-enzyme 129 | */ 130 | type EnzymeMatchersType = { 131 | toBeChecked(): void, 132 | toBeDisabled(): void, 133 | toBeEmpty(): void, 134 | toBeEmptyRender(): void, 135 | toBePresent(): void, 136 | toContainReact(element: React$Element): void, 137 | toExist(): void, 138 | toHaveClassName(className: string): void, 139 | toHaveHTML(html: string): void, 140 | toHaveProp: ((propKey: string, propValue?: any) => void) & ((props: Object) => void), 141 | toHaveRef(refName: string): void, 142 | toHaveState: ((stateKey: string, stateValue?: any) => void) & ((state: Object) => void), 143 | toHaveStyle: ((styleKey: string, styleValue?: any) => void) & ((style: Object) => void), 144 | toHaveTagName(tagName: string): void, 145 | toHaveText(text: string): void, 146 | toIncludeText(text: string): void, 147 | toHaveValue(value: any): void, 148 | toMatchElement(element: React$Element): void, 149 | toMatchSelector(selector: string): void 150 | }; 151 | 152 | // DOM testing library extensions https://github.com/kentcdodds/dom-testing-library#custom-jest-matchers 153 | type DomTestingLibraryType = { 154 | toBeInTheDOM(): void, 155 | toHaveTextContent(content: string): void, 156 | toHaveAttribute(name: string, expectedValue?: string): void 157 | }; 158 | 159 | // Jest JQuery Matchers: https://github.com/unindented/custom-jquery-matchers 160 | type JestJQueryMatchersType = { 161 | toExist(): void, 162 | toHaveLength(len: number): void, 163 | toHaveId(id: string): void, 164 | toHaveClass(className: string): void, 165 | toHaveTag(tag: string): void, 166 | toHaveAttr(key: string, val?: any): void, 167 | toHaveProp(key: string, val?: any): void, 168 | toHaveText(text: string | RegExp): void, 169 | toHaveData(key: string, val?: any): void, 170 | toHaveValue(val: any): void, 171 | toHaveCss(css: {[key: string]: any}): void, 172 | toBeChecked(): void, 173 | toBeDisabled(): void, 174 | toBeEmpty(): void, 175 | toBeHidden(): void, 176 | toBeSelected(): void, 177 | toBeVisible(): void, 178 | toBeFocused(): void, 179 | toBeInDom(): void, 180 | toBeMatchedBy(sel: string): void, 181 | toHaveDescendant(sel: string): void, 182 | toHaveDescendantWithText(sel: string, text: string | RegExp): void 183 | }; 184 | 185 | 186 | // Jest Extended Matchers: https://github.com/jest-community/jest-extended 187 | type JestExtendedMatchersType = { 188 | /** 189 | * Note: Currently unimplemented 190 | * Passing assertion 191 | * 192 | * @param {String} message 193 | */ 194 | // pass(message: string): void; 195 | 196 | /** 197 | * Note: Currently unimplemented 198 | * Failing assertion 199 | * 200 | * @param {String} message 201 | */ 202 | // fail(message: string): void; 203 | 204 | /** 205 | * Use .toBeEmpty when checking if a String '', Array [] or Object {} is empty. 206 | */ 207 | toBeEmpty(): void; 208 | 209 | /** 210 | * Use .toBeOneOf when checking if a value is a member of a given Array. 211 | * @param {Array.<*>} members 212 | */ 213 | toBeOneOf(members: any[]): void; 214 | 215 | /** 216 | * Use `.toBeNil` when checking a value is `null` or `undefined`. 217 | */ 218 | toBeNil(): void; 219 | 220 | /** 221 | * Use `.toSatisfy` when you want to use a custom matcher by supplying a predicate function that returns a `Boolean`. 222 | * @param {Function} predicate 223 | */ 224 | toSatisfy(predicate: (n: any) => boolean): void; 225 | 226 | /** 227 | * Use `.toBeArray` when checking if a value is an `Array`. 228 | */ 229 | toBeArray(): void; 230 | 231 | /** 232 | * Use `.toBeArrayOfSize` when checking if a value is an `Array` of size x. 233 | * @param {Number} x 234 | */ 235 | toBeArrayOfSize(x: number): void; 236 | 237 | /** 238 | * Use `.toIncludeAllMembers` when checking if an `Array` contains all of the same members of a given set. 239 | * @param {Array.<*>} members 240 | */ 241 | toIncludeAllMembers(members: any[]): void; 242 | 243 | /** 244 | * Use `.toIncludeAnyMembers` when checking if an `Array` contains any of the members of a given set. 245 | * @param {Array.<*>} members 246 | */ 247 | toIncludeAnyMembers(members: any[]): void; 248 | 249 | /** 250 | * Use `.toSatisfyAll` when you want to use a custom matcher by supplying a predicate function that returns a `Boolean` for all values in an array. 251 | * @param {Function} predicate 252 | */ 253 | toSatisfyAll(predicate: (n: any) => boolean): void; 254 | 255 | /** 256 | * Use `.toBeBoolean` when checking if a value is a `Boolean`. 257 | */ 258 | toBeBoolean(): void; 259 | 260 | /** 261 | * Use `.toBeTrue` when checking a value is equal (===) to `true`. 262 | */ 263 | toBeTrue(): void; 264 | 265 | /** 266 | * Use `.toBeFalse` when checking a value is equal (===) to `false`. 267 | */ 268 | toBeFalse(): void; 269 | 270 | /** 271 | * Use .toBeDate when checking if a value is a Date. 272 | */ 273 | toBeDate(): void; 274 | 275 | /** 276 | * Use `.toBeFunction` when checking if a value is a `Function`. 277 | */ 278 | toBeFunction(): void; 279 | 280 | /** 281 | * Use `.toHaveBeenCalledBefore` when checking if a `Mock` was called before another `Mock`. 282 | * 283 | * Note: Required Jest version >22 284 | * Note: Your mock functions will have to be asynchronous to cause the timestamps inside of Jest to occur in a differentJS event loop, otherwise the mock timestamps will all be the same 285 | * 286 | * @param {Mock} mock 287 | */ 288 | toHaveBeenCalledBefore(mock: JestMockFn): void; 289 | 290 | /** 291 | * Use `.toBeNumber` when checking if a value is a `Number`. 292 | */ 293 | toBeNumber(): void; 294 | 295 | /** 296 | * Use `.toBeNaN` when checking a value is `NaN`. 297 | */ 298 | toBeNaN(): void; 299 | 300 | /** 301 | * Use `.toBeFinite` when checking if a value is a `Number`, not `NaN` or `Infinity`. 302 | */ 303 | toBeFinite(): void; 304 | 305 | /** 306 | * Use `.toBePositive` when checking if a value is a positive `Number`. 307 | */ 308 | toBePositive(): void; 309 | 310 | /** 311 | * Use `.toBeNegative` when checking if a value is a negative `Number`. 312 | */ 313 | toBeNegative(): void; 314 | 315 | /** 316 | * Use `.toBeEven` when checking if a value is an even `Number`. 317 | */ 318 | toBeEven(): void; 319 | 320 | /** 321 | * Use `.toBeOdd` when checking if a value is an odd `Number`. 322 | */ 323 | toBeOdd(): void; 324 | 325 | /** 326 | * Use `.toBeWithin` when checking if a number is in between the given bounds of: start (inclusive) and end (exclusive). 327 | * 328 | * @param {Number} start 329 | * @param {Number} end 330 | */ 331 | toBeWithin(start: number, end: number): void; 332 | 333 | /** 334 | * Use `.toBeObject` when checking if a value is an `Object`. 335 | */ 336 | toBeObject(): void; 337 | 338 | /** 339 | * Use `.toContainKey` when checking if an object contains the provided key. 340 | * 341 | * @param {String} key 342 | */ 343 | toContainKey(key: string): void; 344 | 345 | /** 346 | * Use `.toContainKeys` when checking if an object has all of the provided keys. 347 | * 348 | * @param {Array.} keys 349 | */ 350 | toContainKeys(keys: string[]): void; 351 | 352 | /** 353 | * Use `.toContainAllKeys` when checking if an object only contains all of the provided keys. 354 | * 355 | * @param {Array.} keys 356 | */ 357 | toContainAllKeys(keys: string[]): void; 358 | 359 | /** 360 | * Use `.toContainAnyKeys` when checking if an object contains at least one of the provided keys. 361 | * 362 | * @param {Array.} keys 363 | */ 364 | toContainAnyKeys(keys: string[]): void; 365 | 366 | /** 367 | * Use `.toContainValue` when checking if an object contains the provided value. 368 | * 369 | * @param {*} value 370 | */ 371 | toContainValue(value: any): void; 372 | 373 | /** 374 | * Use `.toContainValues` when checking if an object contains all of the provided values. 375 | * 376 | * @param {Array.<*>} values 377 | */ 378 | toContainValues(values: any[]): void; 379 | 380 | /** 381 | * Use `.toContainAllValues` when checking if an object only contains all of the provided values. 382 | * 383 | * @param {Array.<*>} values 384 | */ 385 | toContainAllValues(values: any[]): void; 386 | 387 | /** 388 | * Use `.toContainAnyValues` when checking if an object contains at least one of the provided values. 389 | * 390 | * @param {Array.<*>} values 391 | */ 392 | toContainAnyValues(values: any[]): void; 393 | 394 | /** 395 | * Use `.toContainEntry` when checking if an object contains the provided entry. 396 | * 397 | * @param {Array.} entry 398 | */ 399 | toContainEntry(entry: [string, string]): void; 400 | 401 | /** 402 | * Use `.toContainEntries` when checking if an object contains all of the provided entries. 403 | * 404 | * @param {Array.>} entries 405 | */ 406 | toContainEntries(entries: [string, string][]): void; 407 | 408 | /** 409 | * Use `.toContainAllEntries` when checking if an object only contains all of the provided entries. 410 | * 411 | * @param {Array.>} entries 412 | */ 413 | toContainAllEntries(entries: [string, string][]): void; 414 | 415 | /** 416 | * Use `.toContainAnyEntries` when checking if an object contains at least one of the provided entries. 417 | * 418 | * @param {Array.>} entries 419 | */ 420 | toContainAnyEntries(entries: [string, string][]): void; 421 | 422 | /** 423 | * Use `.toBeExtensible` when checking if an object is extensible. 424 | */ 425 | toBeExtensible(): void; 426 | 427 | /** 428 | * Use `.toBeFrozen` when checking if an object is frozen. 429 | */ 430 | toBeFrozen(): void; 431 | 432 | /** 433 | * Use `.toBeSealed` when checking if an object is sealed. 434 | */ 435 | toBeSealed(): void; 436 | 437 | /** 438 | * Use `.toBeString` when checking if a value is a `String`. 439 | */ 440 | toBeString(): void; 441 | 442 | /** 443 | * Use `.toEqualCaseInsensitive` when checking if a string is equal (===) to another ignoring the casing of both strings. 444 | * 445 | * @param {String} string 446 | */ 447 | toEqualCaseInsensitive(string: string): void; 448 | 449 | /** 450 | * Use `.toStartWith` when checking if a `String` starts with a given `String` prefix. 451 | * 452 | * @param {String} prefix 453 | */ 454 | toStartWith(prefix: string): void; 455 | 456 | /** 457 | * Use `.toEndWith` when checking if a `String` ends with a given `String` suffix. 458 | * 459 | * @param {String} suffix 460 | */ 461 | toEndWith(suffix: string): void; 462 | 463 | /** 464 | * Use `.toInclude` when checking if a `String` includes the given `String` substring. 465 | * 466 | * @param {String} substring 467 | */ 468 | toInclude(substring: string): void; 469 | 470 | /** 471 | * Use `.toIncludeRepeated` when checking if a `String` includes the given `String` substring the correct number of times. 472 | * 473 | * @param {String} substring 474 | * @param {Number} times 475 | */ 476 | toIncludeRepeated(substring: string, times: number): void; 477 | 478 | /** 479 | * Use `.toIncludeMultiple` when checking if a `String` includes all of the given substrings. 480 | * 481 | * @param {Array.} substring 482 | */ 483 | toIncludeMultiple(substring: string[]): void; 484 | }; 485 | 486 | type JestExpectType = { 487 | not: JestExpectType & EnzymeMatchersType & DomTestingLibraryType & JestJQueryMatchersType & JestExtendedMatchersType, 488 | /** 489 | * If you have a mock function, you can use .lastCalledWith to test what 490 | * arguments it was last called with. 491 | */ 492 | lastCalledWith(...args: Array): void, 493 | /** 494 | * toBe just checks that a value is what you expect. It uses === to check 495 | * strict equality. 496 | */ 497 | toBe(value: any): void, 498 | /** 499 | * Use .toHaveBeenCalled to ensure that a mock function got called. 500 | */ 501 | toBeCalled(): void, 502 | /** 503 | * Use .toBeCalledWith to ensure that a mock function was called with 504 | * specific arguments. 505 | */ 506 | toBeCalledWith(...args: Array): void, 507 | /** 508 | * Using exact equality with floating point numbers is a bad idea. Rounding 509 | * means that intuitive things fail. 510 | */ 511 | toBeCloseTo(num: number, delta: any): void, 512 | /** 513 | * Use .toBeDefined to check that a variable is not undefined. 514 | */ 515 | toBeDefined(): void, 516 | /** 517 | * Use .toBeFalsy when you don't care what a value is, you just want to 518 | * ensure a value is false in a boolean context. 519 | */ 520 | toBeFalsy(): void, 521 | /** 522 | * To compare floating point numbers, you can use toBeGreaterThan. 523 | */ 524 | toBeGreaterThan(number: number): void, 525 | /** 526 | * To compare floating point numbers, you can use toBeGreaterThanOrEqual. 527 | */ 528 | toBeGreaterThanOrEqual(number: number): void, 529 | /** 530 | * To compare floating point numbers, you can use toBeLessThan. 531 | */ 532 | toBeLessThan(number: number): void, 533 | /** 534 | * To compare floating point numbers, you can use toBeLessThanOrEqual. 535 | */ 536 | toBeLessThanOrEqual(number: number): void, 537 | /** 538 | * Use .toBeInstanceOf(Class) to check that an object is an instance of a 539 | * class. 540 | */ 541 | toBeInstanceOf(cls: Class<*>): void, 542 | /** 543 | * .toBeNull() is the same as .toBe(null) but the error messages are a bit 544 | * nicer. 545 | */ 546 | toBeNull(): void, 547 | /** 548 | * Use .toBeTruthy when you don't care what a value is, you just want to 549 | * ensure a value is true in a boolean context. 550 | */ 551 | toBeTruthy(): void, 552 | /** 553 | * Use .toBeUndefined to check that a variable is undefined. 554 | */ 555 | toBeUndefined(): void, 556 | /** 557 | * Use .toContain when you want to check that an item is in a list. For 558 | * testing the items in the list, this uses ===, a strict equality check. 559 | */ 560 | toContain(item: any): void, 561 | /** 562 | * Use .toContainEqual when you want to check that an item is in a list. For 563 | * testing the items in the list, this matcher recursively checks the 564 | * equality of all fields, rather than checking for object identity. 565 | */ 566 | toContainEqual(item: any): void, 567 | /** 568 | * Use .toEqual when you want to check that two objects have the same value. 569 | * This matcher recursively checks the equality of all fields, rather than 570 | * checking for object identity. 571 | */ 572 | toEqual(value: any): void, 573 | /** 574 | * Use .toHaveBeenCalled to ensure that a mock function got called. 575 | */ 576 | toHaveBeenCalled(): void, 577 | /** 578 | * Use .toHaveBeenCalledTimes to ensure that a mock function got called exact 579 | * number of times. 580 | */ 581 | toHaveBeenCalledTimes(number: number): void, 582 | /** 583 | * Use .toHaveBeenCalledWith to ensure that a mock function was called with 584 | * specific arguments. 585 | */ 586 | toHaveBeenCalledWith(...args: Array): void, 587 | /** 588 | * Use .toHaveBeenLastCalledWith to ensure that a mock function was last called 589 | * with specific arguments. 590 | */ 591 | toHaveBeenLastCalledWith(...args: Array): void, 592 | /** 593 | * Check that an object has a .length property and it is set to a certain 594 | * numeric value. 595 | */ 596 | toHaveLength(number: number): void, 597 | /** 598 | * 599 | */ 600 | toHaveProperty(propPath: string | Array, value?: any): void, 601 | /** 602 | * Use .toMatch to check that a string matches a regular expression or string. 603 | */ 604 | toMatch(regexpOrString: RegExp | string): void, 605 | /** 606 | * Use .toMatchObject to check that a javascript object matches a subset of the properties of an object. 607 | */ 608 | toMatchObject(object: Object | Array): void, 609 | /** 610 | * This ensures that a React component matches the most recent snapshot. 611 | */ 612 | toMatchSnapshot(name?: string): void, 613 | /** 614 | * Use .toThrow to test that a function throws when it is called. 615 | * If you want to test that a specific error gets thrown, you can provide an 616 | * argument to toThrow. The argument can be a string for the error message, 617 | * a class for the error, or a regex that should match the error. 618 | * 619 | * Alias: .toThrowError 620 | */ 621 | toThrow(message?: string | Error | Class | RegExp): void, 622 | toThrowError(message?: string | Error | Class | RegExp): void, 623 | /** 624 | * Use .toThrowErrorMatchingSnapshot to test that a function throws a error 625 | * matching the most recent snapshot when it is called. 626 | */ 627 | toThrowErrorMatchingSnapshot(): void 628 | }; 629 | 630 | type JestObjectType = { 631 | /** 632 | * Disables automatic mocking in the module loader. 633 | * 634 | * After this method is called, all `require()`s will return the real 635 | * versions of each module (rather than a mocked version). 636 | */ 637 | disableAutomock(): JestObjectType, 638 | /** 639 | * An un-hoisted version of disableAutomock 640 | */ 641 | autoMockOff(): JestObjectType, 642 | /** 643 | * Enables automatic mocking in the module loader. 644 | */ 645 | enableAutomock(): JestObjectType, 646 | /** 647 | * An un-hoisted version of enableAutomock 648 | */ 649 | autoMockOn(): JestObjectType, 650 | /** 651 | * Clears the mock.calls and mock.instances properties of all mocks. 652 | * Equivalent to calling .mockClear() on every mocked function. 653 | */ 654 | clearAllMocks(): JestObjectType, 655 | /** 656 | * Resets the state of all mocks. Equivalent to calling .mockReset() on every 657 | * mocked function. 658 | */ 659 | resetAllMocks(): JestObjectType, 660 | /** 661 | * Restores all mocks back to their original value. 662 | */ 663 | restoreAllMocks(): JestObjectType, 664 | /** 665 | * Removes any pending timers from the timer system. 666 | */ 667 | clearAllTimers(): void, 668 | /** 669 | * The same as `mock` but not moved to the top of the expectation by 670 | * babel-jest. 671 | */ 672 | doMock(moduleName: string, moduleFactory?: any): JestObjectType, 673 | /** 674 | * The same as `unmock` but not moved to the top of the expectation by 675 | * babel-jest. 676 | */ 677 | dontMock(moduleName: string): JestObjectType, 678 | /** 679 | * Returns a new, unused mock function. Optionally takes a mock 680 | * implementation. 681 | */ 682 | fn, TReturn>( 683 | implementation?: (...args: TArguments) => TReturn 684 | ): JestMockFn, 685 | /** 686 | * Determines if the given function is a mocked function. 687 | */ 688 | isMockFunction(fn: Function): boolean, 689 | /** 690 | * Given the name of a module, use the automatic mocking system to generate a 691 | * mocked version of the module for you. 692 | */ 693 | genMockFromModule(moduleName: string): any, 694 | /** 695 | * Mocks a module with an auto-mocked version when it is being required. 696 | * 697 | * The second argument can be used to specify an explicit module factory that 698 | * is being run instead of using Jest's automocking feature. 699 | * 700 | * The third argument can be used to create virtual mocks -- mocks of modules 701 | * that don't exist anywhere in the system. 702 | */ 703 | mock( 704 | moduleName: string, 705 | moduleFactory?: any, 706 | options?: Object 707 | ): JestObjectType, 708 | /** 709 | * Returns the actual module instead of a mock, bypassing all checks on 710 | * whether the module should receive a mock implementation or not. 711 | */ 712 | requireActual(moduleName: string): any, 713 | /** 714 | * Returns a mock module instead of the actual module, bypassing all checks 715 | * on whether the module should be required normally or not. 716 | */ 717 | requireMock(moduleName: string): any, 718 | /** 719 | * Resets the module registry - the cache of all required modules. This is 720 | * useful to isolate modules where local state might conflict between tests. 721 | */ 722 | resetModules(): JestObjectType, 723 | /** 724 | * Exhausts the micro-task queue (usually interfaced in node via 725 | * process.nextTick). 726 | */ 727 | runAllTicks(): void, 728 | /** 729 | * Exhausts the macro-task queue (i.e., all tasks queued by setTimeout(), 730 | * setInterval(), and setImmediate()). 731 | */ 732 | runAllTimers(): void, 733 | /** 734 | * Exhausts all tasks queued by setImmediate(). 735 | */ 736 | runAllImmediates(): void, 737 | /** 738 | * Executes only the macro task queue (i.e. all tasks queued by setTimeout() 739 | * or setInterval() and setImmediate()). 740 | */ 741 | advanceTimersByTime(msToRun: number): void, 742 | /** 743 | * Executes only the macro task queue (i.e. all tasks queued by setTimeout() 744 | * or setInterval() and setImmediate()). 745 | * 746 | * Renamed to `advanceTimersByTime`. 747 | */ 748 | runTimersToTime(msToRun: number): void, 749 | /** 750 | * Executes only the macro-tasks that are currently pending (i.e., only the 751 | * tasks that have been queued by setTimeout() or setInterval() up to this 752 | * point) 753 | */ 754 | runOnlyPendingTimers(): void, 755 | /** 756 | * Explicitly supplies the mock object that the module system should return 757 | * for the specified module. Note: It is recommended to use jest.mock() 758 | * instead. 759 | */ 760 | setMock(moduleName: string, moduleExports: any): JestObjectType, 761 | /** 762 | * Indicates that the module system should never return a mocked version of 763 | * the specified module from require() (e.g. that it should always return the 764 | * real module). 765 | */ 766 | unmock(moduleName: string): JestObjectType, 767 | /** 768 | * Instructs Jest to use fake versions of the standard timer functions 769 | * (setTimeout, setInterval, clearTimeout, clearInterval, nextTick, 770 | * setImmediate and clearImmediate). 771 | */ 772 | useFakeTimers(): JestObjectType, 773 | /** 774 | * Instructs Jest to use the real versions of the standard timer functions. 775 | */ 776 | useRealTimers(): JestObjectType, 777 | /** 778 | * Creates a mock function similar to jest.fn but also tracks calls to 779 | * object[methodName]. 780 | */ 781 | spyOn(object: Object, methodName: string, accessType?: "get" | "set"): JestMockFn, 782 | /** 783 | * Set the default timeout interval for tests and before/after hooks in milliseconds. 784 | * Note: The default timeout interval is 5 seconds if this method is not called. 785 | */ 786 | setTimeout(timeout: number): JestObjectType 787 | }; 788 | 789 | type JestSpyType = { 790 | calls: JestCallsType 791 | }; 792 | 793 | /** Runs this function after every test inside this context */ 794 | declare function afterEach( 795 | fn: (done: () => void) => ?Promise, 796 | timeout?: number 797 | ): void; 798 | /** Runs this function before every test inside this context */ 799 | declare function beforeEach( 800 | fn: (done: () => void) => ?Promise, 801 | timeout?: number 802 | ): void; 803 | /** Runs this function after all tests have finished inside this context */ 804 | declare function afterAll( 805 | fn: (done: () => void) => ?Promise, 806 | timeout?: number 807 | ): void; 808 | /** Runs this function before any tests have started inside this context */ 809 | declare function beforeAll( 810 | fn: (done: () => void) => ?Promise, 811 | timeout?: number 812 | ): void; 813 | 814 | /** A context for grouping tests together */ 815 | declare var describe: { 816 | /** 817 | * Creates a block that groups together several related tests in one "test suite" 818 | */ 819 | (name: JestTestName, fn: () => void): void, 820 | 821 | /** 822 | * Only run this describe block 823 | */ 824 | only(name: JestTestName, fn: () => void): void, 825 | 826 | /** 827 | * Skip running this describe block 828 | */ 829 | skip(name: JestTestName, fn: () => void): void 830 | }; 831 | 832 | /** An individual test unit */ 833 | declare var it: { 834 | /** 835 | * An individual test unit 836 | * 837 | * @param {JestTestName} Name of Test 838 | * @param {Function} Test 839 | * @param {number} Timeout for the test, in milliseconds. 840 | */ 841 | ( 842 | name: JestTestName, 843 | fn?: (done: () => void) => ?Promise, 844 | timeout?: number 845 | ): void, 846 | /** 847 | * Only run this test 848 | * 849 | * @param {JestTestName} Name of Test 850 | * @param {Function} Test 851 | * @param {number} Timeout for the test, in milliseconds. 852 | */ 853 | only( 854 | name: JestTestName, 855 | fn?: (done: () => void) => ?Promise, 856 | timeout?: number 857 | ): void, 858 | /** 859 | * Skip running this test 860 | * 861 | * @param {JestTestName} Name of Test 862 | * @param {Function} Test 863 | * @param {number} Timeout for the test, in milliseconds. 864 | */ 865 | skip( 866 | name: JestTestName, 867 | fn?: (done: () => void) => ?Promise, 868 | timeout?: number 869 | ): void, 870 | /** 871 | * Run the test concurrently 872 | * 873 | * @param {JestTestName} Name of Test 874 | * @param {Function} Test 875 | * @param {number} Timeout for the test, in milliseconds. 876 | */ 877 | concurrent( 878 | name: JestTestName, 879 | fn?: (done: () => void) => ?Promise, 880 | timeout?: number 881 | ): void 882 | }; 883 | declare function fit( 884 | name: JestTestName, 885 | fn: (done: () => void) => ?Promise, 886 | timeout?: number 887 | ): void; 888 | /** An individual test unit */ 889 | declare var test: typeof it; 890 | /** A disabled group of tests */ 891 | declare var xdescribe: typeof describe; 892 | /** A focused group of tests */ 893 | declare var fdescribe: typeof describe; 894 | /** A disabled individual test */ 895 | declare var xit: typeof it; 896 | /** A disabled individual test */ 897 | declare var xtest: typeof it; 898 | 899 | type JestPrettyFormatColors = { 900 | comment: { close: string, open: string }, 901 | content: { close: string, open: string }, 902 | prop: { close: string, open: string }, 903 | tag: { close: string, open: string }, 904 | value: { close: string, open: string }, 905 | }; 906 | 907 | type JestPrettyFormatIndent = string => string; 908 | type JestPrettyFormatRefs = Array; 909 | type JestPrettyFormatPrint = any => string; 910 | type JestPrettyFormatStringOrNull = string | null; 911 | 912 | type JestPrettyFormatOptions = {| 913 | callToJSON: boolean, 914 | edgeSpacing: string, 915 | escapeRegex: boolean, 916 | highlight: boolean, 917 | indent: number, 918 | maxDepth: number, 919 | min: boolean, 920 | plugins: JestPrettyFormatPlugins, 921 | printFunctionName: boolean, 922 | spacing: string, 923 | theme: {| 924 | comment: string, 925 | content: string, 926 | prop: string, 927 | tag: string, 928 | value: string, 929 | |}, 930 | |}; 931 | 932 | type JestPrettyFormatPlugin = { 933 | print: ( 934 | val: any, 935 | serialize: JestPrettyFormatPrint, 936 | indent: JestPrettyFormatIndent, 937 | opts: JestPrettyFormatOptions, 938 | colors: JestPrettyFormatColors, 939 | ) => string, 940 | test: any => boolean, 941 | }; 942 | 943 | type JestPrettyFormatPlugins = Array; 944 | 945 | /** The expect function is used every time you want to test a value */ 946 | declare var expect: { 947 | /** The object that you want to make assertions against */ 948 | (value: any): JestExpectType & JestPromiseType & EnzymeMatchersType & DomTestingLibraryType & JestJQueryMatchersType & JestExtendedMatchersType, 949 | /** Add additional Jasmine matchers to Jest's roster */ 950 | extend(matchers: { [name: string]: JestMatcher }): void, 951 | /** Add a module that formats application-specific data structures. */ 952 | addSnapshotSerializer(pluginModule: JestPrettyFormatPlugin): void, 953 | assertions(expectedAssertions: number): void, 954 | hasAssertions(): void, 955 | any(value: mixed): JestAsymmetricEqualityType, 956 | anything(): any, 957 | arrayContaining(value: Array): Array, 958 | objectContaining(value: Object): Object, 959 | /** Matches any received string that contains the exact expected string. */ 960 | stringContaining(value: string): string, 961 | stringMatching(value: string | RegExp): string 962 | }; 963 | 964 | // TODO handle return type 965 | // http://jasmine.github.io/2.4/introduction.html#section-Spies 966 | declare function spyOn(value: mixed, method: string): Object; 967 | 968 | /** Holds all functions related to manipulating test runner */ 969 | declare var jest: JestObjectType; 970 | 971 | /** 972 | * The global Jasmine object, this is generally not exposed as the public API, 973 | * using features inside here could break in later versions of Jest. 974 | */ 975 | declare var jasmine: { 976 | DEFAULT_TIMEOUT_INTERVAL: number, 977 | any(value: mixed): JestAsymmetricEqualityType, 978 | anything(): any, 979 | arrayContaining(value: Array): Array, 980 | clock(): JestClockType, 981 | createSpy(name: string): JestSpyType, 982 | createSpyObj( 983 | baseName: string, 984 | methodNames: Array 985 | ): { [methodName: string]: JestSpyType }, 986 | objectContaining(value: Object): Object, 987 | stringMatching(value: string): string 988 | }; 989 | --------------------------------------------------------------------------------