From fd91bc9be51c17b0c138bfd39cef8a421a03aa6f Mon Sep 17 00:00:00 2001 From: Kevin Jahns Date: Tue, 2 Apr 2019 15:37:32 +0200 Subject: [PATCH] no implicit any --- array.js | 14 ++++++++ binary.test.js | 8 +++++ broadcastchannel.js | 11 ++++++- buffer.js | 14 +++++++- buffer.test.js | 3 ++ console.js | 27 ---------------- diff.test.js | 11 +++++++ dom.js | 19 +++++++++-- encoding.test.js | 55 ++++++++++++++++++++++++++++++-- environment.js | 14 ++++++-- error.js | 7 +++- eventloop.js | 3 ++ eventloop.test.js | 3 ++ iterator.js | 52 ++++++++++++++++++++++++++++++ map.test.js | 13 +++++--- math.test.js | 3 ++ object.test.js | 3 ++ observable.js | 6 +++- package.json | 1 + pair.test.js | 3 ++ prng.js | 4 +++ prng.test.js | 11 +++++++ prng/Mt19937.js | 11 +++++-- prng/README.md | 5 --- prng/Xoroshiro128plus.js | 3 ++ prng/Xorshift32.js | 5 ++- promise.js | 31 ++++++++++++++++-- promise.test.js | 20 ++++++++++-- random.js | 4 +++ random.test.js | 3 ++ sort.test.js | 43 +++++++++++++++++++++++++ statistics.js | 4 +++ statistics.test.js | 5 ++- string.js | 14 ++++++++ string.test.js | 5 ++- testing.js | 69 ++++++++++++++++++++++++++++++++++++---- testing.test.js | 6 +++- time.test.js | 3 ++ tree.js | 1 + tsconfig.json | 4 +-- 40 files changed, 454 insertions(+), 67 deletions(-) delete mode 100644 console.js create mode 100644 iterator.js delete mode 100644 prng/README.md diff --git a/array.js b/array.js index e402c82..8daff66 100644 --- a/array.js +++ b/array.js @@ -1 +1,15 @@ + +/** + * Return the last element of an array. The element must exist + * + * @template T + * @param {Array} arr + * @return {T} + */ export const last = arr => arr[arr.length - 1] + +/** + * @template T + * @return {Array} + */ +export const create = () => [] diff --git a/binary.test.js b/binary.test.js index fcb0a23..b316c58 100644 --- a/binary.test.js +++ b/binary.test.js @@ -2,16 +2,24 @@ import * as binary from './binary.js' import * as t from './testing.js' +/** + * @param {t.TestCase} tc + */ export const testBitx = tc => { for (let i = 1; i <= 32; i++) { + // @ts-ignore t.assert(binary[`BIT${i}`] === (1 << (i - 1)), `BIT${i}=${1 << (i - 1)}`) } } +/** + * @param {t.TestCase} tc + */ export const testBitsx = tc => { t.assert(binary.BITS0 === 0) for (let i = 1; i < 32; i++) { const expected = (1 << i) - 1 + // @ts-ignore const have = binary[`BITS${i}`] t.assert(have === expected, `BITS${i}=${have}=${expected}`) } diff --git a/broadcastchannel.js b/broadcastchannel.js index 1844b09..9e71f1c 100644 --- a/broadcastchannel.js +++ b/broadcastchannel.js @@ -15,8 +15,14 @@ import * as buffer from './buffer.js' const channels = new Map() class LocalStoragePolyfill { + /** + * @param {string} room + */ constructor (room) { this.room = room + /** + * @type {null|function({data:ArrayBuffer}):void} + */ this.onmessage = null addEventListener('storage', e => e.key === room && this.onmessage !== null && this.onmessage({ data: buffer.fromBase64(e.newValue || '') })) } @@ -38,9 +44,12 @@ const BC = typeof BroadcastChannel === 'undefined' ? LocalStoragePolyfill : Broa * @return {Channel} */ const getChannel = room => - map.setTfUndefined(channels, room, () => { + map.setIfUndefined(channels, room, () => { const subs = new Set() const bc = new BC(room) + /** + * @param {{data:ArrayBuffer}} e + */ bc.onmessage = e => subs.forEach(sub => sub(e.data)) return { bc, subs diff --git a/buffer.js b/buffer.js index 675a0d9..260f4c7 100644 --- a/buffer.js +++ b/buffer.js @@ -1,17 +1,26 @@ import * as string from './string.js' import * as env from './environment.js' +/** + * @param {number} len + */ export const createUint8ArrayFromLen = len => new Uint8Array(len) /** * Create Uint8Array with initial content from buffer + * + * @param {ArrayBuffer} buffer + * @param {number} byteOffset + * @param {number} length */ export const createUint8ArrayViewFromArrayBuffer = (buffer, byteOffset, length) => new Uint8Array(buffer, byteOffset, length) /** * Create Uint8Array with initial content from buffer + * + * @param {ArrayBuffer} buffer */ -export const createUint8ArrayFromArrayBuffer = arraybuffer => new Uint8Array(arraybuffer) +export const createUint8ArrayFromArrayBuffer = buffer => new Uint8Array(buffer) /* istanbul ignore next */ /** @@ -48,6 +57,9 @@ const fromBase64Browser = s => { return bytes } +/** + * @param {string} s + */ const fromBase64Node = s => new Uint8Array(Buffer.from(s, 'base64').buffer) /* istanbul ignore next */ diff --git a/buffer.test.js b/buffer.test.js index 7c0f430..bf1ceba 100644 --- a/buffer.test.js +++ b/buffer.test.js @@ -2,6 +2,9 @@ import * as t from './testing.js' import * as buffer from './buffer.js' import * as prng from './prng.js' +/** + * @param {t.TestCase} tc + */ export const testRepeatBase64Encoding = tc => { const gen = tc.prng const barr = prng.uint8Array(gen, 100000) diff --git a/console.js b/console.js deleted file mode 100644 index ad12408..0000000 --- a/console.js +++ /dev/null @@ -1,27 +0,0 @@ -/** - * @module logging - */ - -import * as time from './time.js' -import * as buffer from './buffer.js' - -let date = time.getUnixTime() - -const writeDate = () => { - const oldDate = date - date = time.getUnixTime() - return date - oldDate -} - -export const print = console.log -export const log = m => print(`%cydb-client: %c${m} %c+${writeDate()}ms`, 'color: blue;', '', 'color: blue') - -export const group = console.group -export const groupCollapsed = console.groupCollapsed -export const groupEnd = console.groupEnd - -/** - * @param {ArrayBuffer} buf - * @return {string} - */ -export const arrayBufferToString = buf => JSON.stringify(Array.from(buffer.createUint8ArrayFromArrayBuffer(buf))) diff --git a/diff.test.js b/diff.test.js index a834e9b..a8fc64b 100644 --- a/diff.test.js +++ b/diff.test.js @@ -2,11 +2,19 @@ import { simpleDiff } from './diff.js' import * as prng from './prng.js' import * as t from './testing.js' +/** + * @param {string} a + * @param {string} b + * @param {{pos: number,remove:number,insert:string}} expected + */ function runDiffTest (a, b, expected) { let result = simpleDiff(a, b) t.compare(result, expected) } +/** + * @param {t.TestCase} tc + */ export const testDiffing = tc => { runDiffTest('abc', 'axc', { pos: 1, remove: 1, insert: 'x' }) runDiffTest('bc', 'xc', { pos: 0, remove: 1, insert: 'x' }) @@ -18,6 +26,9 @@ export const testDiffing = tc => { runDiffTest('ax', 'axy', { pos: 2, remove: 0, insert: 'y' }) } +/** + * @param {t.TestCase} tc + */ export const testRepeatDiffing = tc => { const a = prng.word(tc.prng) const b = prng.word(tc.prng) diff --git a/dom.js b/dom.js index 3965868..22680a6 100644 --- a/dom.js +++ b/dom.js @@ -49,12 +49,23 @@ export const append = (parent, nodes) => { return parent } +/** + * @param {EventTarget} el + * @param {string} name + * @param {EventListener} f + */ export const addEventListener = (el, name, f) => el.addEventListener(name, f) + +/** + * @param {EventTarget} el + * @param {string} name + * @param {EventListener} f + */ export const removeEventListener = (el, name, f) => el.removeEventListener(name, f) /** * @param {Node} node - * @param {Array>} listeners + * @param {Array>} listeners * @return {Node} */ export const addEventListeners = (node, listeners) => { @@ -64,7 +75,7 @@ export const addEventListeners = (node, listeners) => { /** * @param {Node} node - * @param {Array>} listeners + * @param {Array>} listeners * @return {Node} */ export const removeEventListeners = (node, listeners) => { @@ -92,6 +103,10 @@ export const canvas = (width, height) => { return c } +/** + * @param {string} t + * @return {Text} + */ export const text = t => document.createTextNode(t) /** diff --git a/encoding.test.js b/encoding.test.js index 41ebbc6..5178593 100644 --- a/encoding.test.js +++ b/encoding.test.js @@ -46,6 +46,14 @@ export const testGolangBinaryEncodingCompatibility = () => { }) } +/** + * @template T + * @param {string} testname + * @param {function(encoding.Encoder, T):void} write + * @param {function(decoding.Decoder):T} read + * @param {T} val + * @param {boolean} doLog + */ function test (testname, write, read, val, doLog = true) { let encoder = encoding.createEncoder() write(encoder, val) @@ -56,14 +64,17 @@ function test (testname, write, read, val, doLog = true) { if (doLog) { t.describe(testname, ` utf8 encode: ${utf8ByteLength} bytes / binary encode: ${binaryByteLength} bytes`) } - t.compareStrings(val + '', result + '') + t.compare(val, result) return { utf8ByteLength, binaryByteLength } } -const testVarString = (s) => { +/** + * @param {string} s + */ +const testVarString = s => { const encoder = encoding.createEncoder() encoding.writeVarString(encoder, s) const decoder = decoding.createDecoder(encoding.toBuffer(encoder)) @@ -84,16 +95,25 @@ export const testVarUintEncoding = tc => { test('varUint of 2839012934', encoding.writeVarUint, decoding.readVarUint, 2839012934) } +/** + * @param {t.TestCase} tc + */ export const testRepeatVarUintEncoding = tc => { const n = prng.int31(tc.prng, 0, (1 << 28) - 1) test(`varUint of ${n}`, encoding.writeVarUint, decoding.readVarUint, n, false) } +/** + * @param {t.TestCase} tc + */ export const testRepeatPeekVarUintEncoding = tc => { const n = prng.int31(tc.prng, 0, (1 << 28) - 1) test(`varUint of ${n}`, encoding.writeVarUint, decoding.peekVarUint, n, false) } +/** + * @param {t.TestCase} tc + */ export const testStringEncoding = tc => { testVarString('hello') testVarString('test!') @@ -105,9 +125,15 @@ export const testStringEncoding = tc => { testVarString('😝') // surrogate length 4 } +/** + * @param {t.TestCase} tc + */ export const testRepeatStringEncoding = tc => testVarString(prng.utf16String(tc.prng)) +/** + * @param {t.TestCase} tc + */ export const testSetMethods = tc => { const encoder = encoding.createEncoder() encoding.writeUint8(encoder, 1) @@ -130,7 +156,15 @@ const defLen = 1000 const loops = 10000 /** - * @type {Array} + * @typedef {Object} EncodingPair + * @property {function(decoding.Decoder):any} EncodingPair.read + * @property {function(encoding.Encoder,any):void} EncodingPair.write + * @property {function(prng.PRNG):any} EncodingPair.gen + */ + +/** + * @template T + * @type {Array} */ const encodingPairs = [ { read: decoder => decoding.readArrayBuffer(decoder, defLen), write: encoding.writeArrayBuffer, gen: gen => prng.arrayBuffer(gen, defLen) }, @@ -142,6 +176,9 @@ const encodingPairs = [ { read: decoding.readVarUint, write: encoding.writeVarUint, gen: gen => prng.uint53(gen, 0, binary.BITS31) } ] +/** + * @param {t.TestCase} tc + */ export const testRepeatRandomWrites = tc => { t.describe(`Writing ${loops} random values`, `defLen=${defLen}`) const gen = tc.prng @@ -169,6 +206,9 @@ export const testRepeatRandomWrites = tc => { t.compare(tailData, decoding.readTail(decoder)) } +/** + * @param {t.TestCase} tc + */ export const testSetOnOverflow = tc => { const encoder = encoding.createEncoder() const initialLen = encoder.cbuf.byteLength @@ -192,6 +232,9 @@ export const testSetOnOverflow = tc => { t.assert(buffer.createUint8ArrayFromArrayBuffer(buf2)[initialLen + 1] === 7) } +/** + * @param {t.TestCase} tc + */ export const testCloneDecoder = tc => { const encoder = encoding.createEncoder() encoding.writeUint8(encoder, 12132) @@ -206,6 +249,9 @@ export const testCloneDecoder = tc => { t.compare(payload1, payload2) } +/** + * @param {t.TestCase} tc + */ export const testWriteBinaryEncoder = tc => { const encoder = encoding.createEncoder() encoding.writeUint16(encoder, 4) @@ -218,6 +264,9 @@ export const testWriteBinaryEncoder = tc => { t.assert(decoding.readUint16(decoder) === 4) } +/** + * @param {t.TestCase} tc + */ export const testOverflowStringDecoding = tc => { const gen = tc.prng const encoder = encoding.createEncoder() diff --git a/environment.js b/environment.js index c3cd749..3598606 100644 --- a/environment.js +++ b/environment.js @@ -59,9 +59,19 @@ const computeParamsBrowser = () => { /* istanbul ignore next */ const computeParams = isNode ? computeParamsNode : computeParamsBrowser +/** + * @param {string} name + * @return {boolean} + */ export const hasParam = name => computeParams().has(name) + /* istanbul ignore next */ -export const getParam = (name, defaultVal = null) => computeParams().get(name) || defaultVal +/** + * @param {string} name + * @param {string} defaultVal + * @return {string} + */ +export const getParam = (name, defaultVal) => computeParams().get(name) || defaultVal // export const getArgs = name => computeParams() && args -export const production = getParam('production', false) +export const production = getParam('production', '0') !== '0' diff --git a/error.js b/error.js index 2062341..c130429 100644 --- a/error.js +++ b/error.js @@ -1,4 +1,3 @@ -import * as f from './function.js' /** * @throws @@ -15,3 +14,9 @@ export const methodUnimplemented = () => { export const unexpectedCase = () => { throw new Error('Unexpected case') } + +/** + * @param {string} s + * @return {Error} + */ +export const create = s => new Error(s) diff --git a/eventloop.js b/eventloop.js index c629805..bb55b91 100644 --- a/eventloop.js +++ b/eventloop.js @@ -10,6 +10,9 @@ const _runQueue = () => { queue = [] } +/** + * @param {function():void} f + */ export const enqueue = f => { queue.push(f) if (queue.length === 1) { diff --git a/eventloop.test.js b/eventloop.test.js index 436b4aa..4f1e056 100644 --- a/eventloop.test.js +++ b/eventloop.test.js @@ -2,6 +2,9 @@ import * as eventloop from './eventloop.js' import * as t from './testing.js' import * as promise from './promise.js' +/** + * @param {t.TestCase} tc + */ export const testEventloopOrder = tc => { let currI = 0 for (let i = 0; i < 10; i++) { diff --git a/iterator.js b/iterator.js new file mode 100644 index 0000000..4f86881 --- /dev/null +++ b/iterator.js @@ -0,0 +1,52 @@ +/** + * @template T,R + * @param {Iterator} iterator + * @param {function(T):R} f + * @return {Iterator} + */ +export const mapIterator = (iterator, f) => ({ + [Symbol.iterator] () { + return this + }, + // @ts-ignore + next () { + const r = iterator.next() + return r.done ? { value: r.done ? undefined : f(r.value), done: r.done } : { value: f(r.value), done: false } + } +}) + +/** + * @template T + * @param {function():{done:boolean,value:T|undefined}} next + * @return {Iterator} + */ +export const createIterator = next => ({ + [Symbol.iterator] () { + return this + }, + // @ts-ignore + next +}) + +/** + * @template T + * @param {Iterator} iterator + * @param {function(T):boolean} filter + */ +export const iteratorFilter = (iterator, filter) => createIterator(() => { + let res + do { + res = iterator.next() + } while (!res.done && !filter(res.value)) + return res +}) + +/** + * @template T,M + * @param {Iterator} iterator + * @param {function(T):M} fmap + */ +export const iteratorMap = (iterator, fmap) => createIterator(() => { + const { done, value } = iterator.next() + return { done, value: done ? undefined : fmap(value) } +}) diff --git a/map.test.js b/map.test.js index 4bda9fd..1e8fb0b 100644 --- a/map.test.js +++ b/map.test.js @@ -2,18 +2,21 @@ import * as map from './map.js' import * as math from './math.js' import * as t from './testing.js' +/** + * @param {t.TestCase} tc + */ export const testMap = tc => { const m = map.create() m.set(1, 2) m.set(2, 3) t.assert(map.map(m, (value, key) => value * 2 + key).reduce(math.add) === 13) let numberOfWrites = 0 - const createT = i => { + const createT = () => { numberOfWrites++ - return i + 1 + return {} } - map.setTfUndefined(m, 3, createT) - map.setTfUndefined(m, 3, createT) - map.setTfUndefined(m, 3, createT) + map.setIfUndefined(m, 3, createT) + map.setIfUndefined(m, 3, createT) + map.setIfUndefined(m, 3, createT) t.assert(numberOfWrites === 1) } diff --git a/math.test.js b/math.test.js index dcf8a55..b4fde89 100644 --- a/math.test.js +++ b/math.test.js @@ -1,6 +1,9 @@ import * as t from './testing.js' import * as math from './math.js' +/** + * @param {t.TestCase} tc + */ export const testMath = tc => { t.describe('math.abs') t.assert(math.abs(-1) === 1) diff --git a/object.test.js b/object.test.js index d4fea83..6018c27 100644 --- a/object.test.js +++ b/object.test.js @@ -2,6 +2,9 @@ import * as t from './testing.js' import * as object from './object.js' import * as math from './math.js' +/** + * @param {t.TestCase} tc + */ export const testObject = tc => { t.assert(object.create().constructor === undefined, 'object.create creates an empty object without constructor') t.describe('object.equalFlat') diff --git a/observable.js b/observable.js index ff65ea6..d100ebc 100644 --- a/observable.js +++ b/observable.js @@ -20,7 +20,7 @@ export class Observable { * @param {function} f */ on (name, f) { - map.setTfUndefined(this._observers, name, set.create).add(f) + map.setIfUndefined(this._observers, name, set.create).add(f) } /** @@ -28,6 +28,9 @@ export class Observable { * @param {function} f */ once (name, f) { + /** + * @param {...any} args + */ const _f = (...args) => { this.off(name, f) f(...args) @@ -57,6 +60,7 @@ export class Observable { * @param {Array} args The arguments that are applied to the event listener. */ emit (name, args) { + // @ts-ignore return (this._observers.get(name) || map.create()).forEach(f => f(...args)) } diff --git a/package.json b/package.json index 46abc4a..58063f5 100644 --- a/package.json +++ b/package.json @@ -4,6 +4,7 @@ "description": "", "dependencies": {}, "devDependencies": { + "@types/node": "^11.13.0", "fake-indexeddb": "^2.0.5", "live-server": "^1.2.1", "nyc": "^13.3.0", diff --git a/pair.test.js b/pair.test.js index 0179ac4..899a010 100644 --- a/pair.test.js +++ b/pair.test.js @@ -2,6 +2,9 @@ import * as t from './testing.js' import * as pair from './pair.js' import * as math from './math.js' +/** + * @param {t.TestCase} tc + */ export const testPair = tc => { const ps = [pair.create(1, 2), pair.create(3, 4), pair.createReversed(6, 5)] t.describe('Counting elements in pair list') diff --git a/prng.js b/prng.js index 4288d7b..bfb3e31 100644 --- a/prng.js +++ b/prng.js @@ -1,5 +1,9 @@ /** * @module prng + * + * Given a seed a PRNG generates a sequence of numbers that cannot be reasonably predicted. + * Two PRNGs must generate the same random sequence of numbers if given the same seed. + * */ import * as binary from './binary.js' diff --git a/prng.test.js b/prng.test.js index 559359b..64a3964 100644 --- a/prng.test.js +++ b/prng.test.js @@ -173,10 +173,18 @@ const runGenTest = (tc, gen) => { * @param {t.TestCase} tc */ export const testGeneratorXoroshiro128plus = tc => runGenTest(tc, new Xoroshiro128plus(tc.seed)) + +/** + * @param {t.TestCase} tc + */ export const testGeneratorXorshift32 = tc => { t.skip(!production) runGenTest(tc, new Xorshift32(tc.seed)) } + +/** + * @param {t.TestCase} tc + */ export const testGeneratorMt19937 = tc => { t.skip(!production) runGenTest(tc, new Mt19937(tc.seed)) @@ -204,6 +212,9 @@ const printDistribution = (gen, tc) => { } /* istanbul ignore next */ +/** + * @param {t.TestCase} tc + */ export const testNumberDistributions = tc => { t.skip(!isBrowser) t.group('Xoroshiro128plus', () => printDistribution(new Xoroshiro128plus(tc.seed), tc)) diff --git a/prng/Mt19937.js b/prng/Mt19937.js index eb9a2ee..071356f 100644 --- a/prng/Mt19937.js +++ b/prng/Mt19937.js @@ -4,9 +4,16 @@ const N = 624 const M = 397 +/** + * @param {number} u + * @param {number} v + */ const twist = (u, v) => ((((u & 0x80000000) | (v & 0x7fffffff)) >>> 1) ^ ((v & 1) ? 0x9908b0df : 0)) -const nextState = (state) => { +/** + * @param {Uint32Array} state + */ +const nextState = state => { let p = 0 let j for (j = N - M + 1; --j; p++) { @@ -32,7 +39,7 @@ const nextState = (state) => { */ export class Mt19937 { /** - * @param {Number} seed The starting point for the random number generation. If you use the same seed, the generator will return the same sequence of random numbers. + * @param {number} seed Unsigned 32 bit number */ constructor (seed) { this.seed = seed diff --git a/prng/README.md b/prng/README.md deleted file mode 100644 index a7de088..0000000 --- a/prng/README.md +++ /dev/null @@ -1,5 +0,0 @@ -# Pseudo Random Number Generators (PRNG) - -Given a seed a PRNG generates a sequence of numbers that cannot be reasonably predicted. Two PRNGs must generate the same random sequence of numbers if given the same seed. - -TODO: explain what POINT is \ No newline at end of file diff --git a/prng/Xoroshiro128plus.js b/prng/Xoroshiro128plus.js index a5d5aad..8ee9723 100644 --- a/prng/Xoroshiro128plus.js +++ b/prng/Xoroshiro128plus.js @@ -18,6 +18,9 @@ import { Xorshift32 } from './Xorshift32.js' * [Reference implementation](http://vigna.di.unimi.it/xorshift/xoroshiro128plus.c) */ export class Xoroshiro128plus { + /** + * @param {number} seed Unsigned 32 bit number + */ constructor (seed) { this.seed = seed // This is a variant of Xoroshiro128plus to fill the initial state diff --git a/prng/Xorshift32.js b/prng/Xorshift32.js index 8003840..bc7d58c 100644 --- a/prng/Xorshift32.js +++ b/prng/Xorshift32.js @@ -7,10 +7,13 @@ */ export class Xorshift32 { /** - * @param {number} seed The starting point for the random number generation. If you use the same seed, the generator will return the same sequence of random numbers. + * @param {number} seed Unsigned 32 bit number */ constructor (seed) { this.seed = seed + /** + * @type {number} + */ this._state = seed } /** diff --git a/promise.js b/promise.js index 904d22a..a4eaf01 100644 --- a/promise.js +++ b/promise.js @@ -1,15 +1,38 @@ import * as time from './time.js' +/** + * @template T + * @param {function(function():void,function(Error):void):void} f + * @return {Promise} + */ export const create = f => new Promise(f) + /** * `Promise.all` wait for all promises in the array to resolve and return the result - * @param {Array>} arrp - * @return {any} + * @template T + * @param {Array>} arrp + * @return {Promise>} */ export const all = arrp => Promise.all(arrp) + +/** + * @param {Error} [reason] + * @return {Promise} + */ export const reject = reason => Promise.reject(reason) + +/** + * @template T + * @param {T|void} res + * @return {Promise} + */ export const resolve = res => Promise.resolve(res) +/** + * @param {number} timeout + * @param {function():boolean} check + * @return {Promise} + */ export const until = (timeout, check) => create((resolve, reject) => { const startTime = time.getUnixTime() const hasTimeout = timeout > 0 @@ -27,4 +50,8 @@ export const until = (timeout, check) => create((resolve, reject) => { const intervalHandle = setInterval(untilInterval, 10) }) +/** + * @param {number} timeout + * @return {Promise} + */ export const wait = timeout => create((resolve, reject) => setTimeout(resolve, timeout)) diff --git a/promise.test.js b/promise.test.js index 4f74002..2246192 100644 --- a/promise.test.js +++ b/promise.test.js @@ -1,7 +1,13 @@ import * as promise from './promise.js' import * as t from './testing.js' import * as time from './time.js' +import * as error from './error.js' +/** + * @param {Promise} p + * @param {number} min + * @param {number} max + */ const measureP = (p, min, max) => { const start = time.getUnixTime() return p.then(() => { @@ -11,16 +17,24 @@ const measureP = (p, min, max) => { }) } -const failsP = p => promise.create((resolve, reject) => p.then(reject, resolve)) +/** + * @template T + * @param {Promise} p + * @return {Promise} + */ +const failsP = p => promise.create((resolve, reject) => p.then(() => reject(error.create('Promise should fail')), resolve)) +/** + * @param {t.TestCase} tc + */ export const testRepeatPromise = async tc => { t.assert(promise.create(r => r()).constructor === Promise, 'p.create() creates a Promise') t.assert(promise.resolve().constructor === Promise, 'p.reject() creates a Promise') - const rejectedP = promise.reject('') + const rejectedP = promise.reject() t.assert(rejectedP.constructor === Promise, 'p.reject() creates a Promise') rejectedP.catch(() => {}) await promise.create(r => r()) - await failsP(promise.reject('')) + await failsP(promise.reject()) await promise.resolve() await measureP(promise.wait(10), 7, 1000) await measureP(failsP(promise.until(15, () => false)), 15, 1000) diff --git a/random.js b/random.js index dc18e67..5111053 100644 --- a/random.js +++ b/random.js @@ -15,6 +15,10 @@ const uint32BrowserCrypto = () => { /* istanbul ignore next */ const uint32NoCrypto = () => Math.ceil((Math.random() * 0xFFFFFFFF) >>> 0) +/** + * @param {typeof import('crypto')} crypto + * @return {function():number} + */ const uint32NodeCrypto = crypto => () => { // @ts-ignore const buf = crypto.randomBytes(4) diff --git a/random.test.js b/random.test.js index e11c2a5..a8856b8 100644 --- a/random.test.js +++ b/random.test.js @@ -2,6 +2,9 @@ import * as random from './random.js' import * as t from './testing.js' import * as binary from './binary.js' +/** + * @param {t.TestCase} tc + */ export const testUint32 = tc => { let num = 0 let newNum = 0 diff --git a/sort.test.js b/sort.test.js index 2313225..42225a7 100644 --- a/sort.test.js +++ b/sort.test.js @@ -33,6 +33,7 @@ const runSortTest = (tc, arr, compare, getVal) => { * @template T * @param {t.TestCase} tc * @param {function(number):Array} createArray + * @param {function(T,T):number} compare 0 if equal, 1 if a { @@ -67,8 +68,21 @@ const createSortTest = (tc, createArray, compare, getVal) => { */ export const testSortUint16 = tc => { t.skip(!t.production) + /** + * @param {number} i + * @return {number} + */ const getVal = i => i + /** + * @param {number} a + * @param {number} b + * @return {number} + */ const compare = (a, b) => a - b + /** + * @param {number} len + * @return {Array} + */ const createArray = len => Array.from(new Uint16Array(prng.arrayBuffer(tc.prng, len * 2))) createSortTest(tc, createArray, compare, getVal) } @@ -78,8 +92,21 @@ export const testSortUint16 = tc => { */ export const testSortUint32 = tc => { t.skip(!t.production) + /** + * @param {number} i + * @return {number} + */ const getVal = i => i + /** + * @param {number} a + * @param {number} b + * @return {number} + */ const compare = (a, b) => a - b + /** + * @param {number} len + * @return {Array} + */ const createArray = len => Array.from(new Uint32Array(prng.arrayBuffer(tc.prng, len * 4))) createSortTest(tc, createArray, compare, getVal) } @@ -88,12 +115,28 @@ export const testSortUint32 = tc => { * @param {t.TestCase} tc */ export const testSortObjectUint32 = tc => { + /** + * @param {{index:number}} obj + * @return {number} + */ const getVal = obj => obj.index + /** + * @param {{index:number}} a + * @param {{index:number}} b + * @return {number} + */ const compare = (a, b) => a.index - b.index + /** + * @param {number} len + * @return {Array<{index:number}>} + */ const createArray = len => Array.from(new Uint32Array(prng.arrayBuffer(tc.prng, len * 4))).map(index => ({ index })) createSortTest(tc, createArray, compare, getVal) } +/** + * @param {t.TestCase} tc + */ export const testListVsArrayPerformance = tc => { /** * @typedef {{ val: number }} Val diff --git a/statistics.js b/statistics.js index b430ff0..47f2fec 100644 --- a/statistics.js +++ b/statistics.js @@ -7,4 +7,8 @@ import * as math from './math.js' */ export const median = arr => arr.length === 0 ? NaN : (arr.length % 2 === 1 ? arr[(arr.length - 1) / 2] : (arr[math.floor((arr.length - 1) / 2)] + arr[math.ceil((arr.length - 1) / 2)]) / 2) +/** + * @param {Array} arr + * @return {number} + */ export const average = arr => arr.reduce(math.add, 0) / arr.length diff --git a/statistics.test.js b/statistics.test.js index b12f4a6..c8dae85 100644 --- a/statistics.test.js +++ b/statistics.test.js @@ -2,7 +2,10 @@ import * as statistics from './statistics.js' import * as t from './testing.js' import * as math from './math.js' -export const testMedian = () => { +/** + * @param {t.TestCase} tc + */ +export const testMedian = tc => { t.assert(math.isNaN(statistics.median([])), 'median([]) = NaN') t.assert(statistics.median([1]) === 1, 'median([x]) = x') t.assert(statistics.median([1, 2, 3]) === 2, 'median([a,b,c]) = b') diff --git a/string.js b/string.js index b53d262..afd4c8a 100644 --- a/string.js +++ b/string.js @@ -5,12 +5,26 @@ export const fromCharCode = String.fromCharCode export const fromCodePoint = String.fromCodePoint +/** + * @param {string} s + * @return {string} + */ const toLowerCase = s => s.toLowerCase() const trimLeftRegex = /^\s*/g + +/** + * @param {string} s + * @return {string} + */ export const trimLeft = s => s.replace(trimLeftRegex, '') const fromCamelCaseRegex = /([A-Z])/g + +/** + * @param {string} s + * @param {string} separator + */ export const fromCamelCase = (s, separator) => trimLeft(s.replace(fromCamelCaseRegex, match => `${separator}${toLowerCase(match)}`)) /** diff --git a/string.test.js b/string.test.js index f447b47..41cbf33 100644 --- a/string.test.js +++ b/string.test.js @@ -1,7 +1,10 @@ import * as string from './string.js' import * as t from './testing.js' -export const testLowercaseTransformation = () => { +/** + * @param {t.TestCase} tc + */ +export const testLowercaseTransformation = tc => { t.compareStrings(string.fromCamelCase('ThisIsATest', ' '), 'this is a test') t.compareStrings(string.fromCamelCase('Testing', ' '), 'testing') t.compareStrings(string.fromCamelCase('testingThis', ' '), 'testing this') diff --git a/testing.js b/testing.js index a34ddfd..119dc75 100644 --- a/testing.js +++ b/testing.js @@ -17,13 +17,14 @@ import * as json from './json.js' export { production } from './environment.js' /* istanbul ignore next */ -export const envSeed = env.hasParam('--seed') ? Number.parseInt(env.getParam('--seed')) : null +export const envSeed = env.hasParam('--seed') ? Number.parseInt(env.getParam('--seed', '0')) : null export class TestCase { + /** + * @param {string} moduleName + * @param {string} testName + */ constructor (moduleName, testName) { - /** - * @type {prng.PRNG} - */ this.moduleName = moduleName this.testName = testName this._seed = null @@ -56,12 +57,20 @@ export class TestCase { const perf = typeof performance === 'undefined' ? require('perf_hooks').performance : performance // eslint-disable-line no-undef export const repititionTime = Number(env.getParam('--repitition-time', '50')) -const testFilter = env.getParam('--filter', null) +const testFilter = env.hasParam('--filter') ? env.getParam('--filter', '') : null + /* istanbul ignore next */ const testFilterRegExp = testFilter !== null ? new RegExp(testFilter) : new RegExp('.*') const repeatTestRegex = /^(repeat|repeating)\s/ +/** + * @param {string} moduleName + * @param {string} name + * @param {function(TestCase):void|Promise} f + * @param {number} i + * @param {number} numberOfTests + */ export const run = async (moduleName, name, f, i, numberOfTests) => { const uncamelized = string.fromCamelCase(name.slice(4), ' ') let filtered = !testFilterRegExp.test(`[${i + 1}/${numberOfTests}] ${moduleName}: ${uncamelized}`) @@ -132,14 +141,25 @@ export const run = async (moduleName, name, f, i, numberOfTests) => { return success } +/** + * @param {string} description + * @param {string} info + */ export const describe = (description, info = '') => log.print(log.BLUE, description, ' ', log.GREY, info) +/** + * @param {string} info + */ export const info = info => describe('', info) export const printDom = log.printDom export const printCanvas = log.printCanvas +/** + * @param {string} description + * @param {function(void):void} f + */ export const group = (description, f) => { log.group(log.BLUE, description) try { @@ -149,6 +169,10 @@ export const group = (description, f) => { } } +/** + * @param {string} message + * @param {function():void} f + */ export const measureTime = (message, f) => { let duration = 0 let iterations = 0 @@ -205,6 +229,7 @@ export const compareStrings = (a, b, m = 'Strings match') => { export const compareObjects = (a, b, m = 'Objects match') => { object.equalFlat(a, b) || fail(m) } /** + * @param {any} constructor * @param {any} a * @param {any} b * @param {string} path @@ -217,12 +242,25 @@ const compareValues = (constructor, a, b, path) => { return true } +/** + * @param {string?} message + * @param {string} reason + * @param {string} path + * @throws {TestError} + */ const _failMessage = (message, reason, path) => fail( message === null ? `${reason} ${path}` : `${message} (${reason}) ${path}` ) +/** + * @param {any} a + * @param {any} b + * @param {string} path + * @param {string?} message + * @param {function(any,any,any,string,any):boolean} customCompare + */ const _compare = (a, b, path, message, customCompare) => { // we don't use assert here because we want to test all branches (istanbul errors if one branch is not tested) if (a == null || b == null) { @@ -251,6 +289,7 @@ const _compare = (a, b, path, message, customCompare) => { if (a.size !== b.size) { _failMessage(message, 'Sets have different number of attributes', path) } + // @ts-ignore a.forEach(value => { if (!b.has(value)) { _failMessage(message, `b.${path} does have ${value}`, path) @@ -262,6 +301,7 @@ const _compare = (a, b, path, message, customCompare) => { if (a.size !== b.size) { _failMessage(message, 'Maps have different number of attributes', path) } + // @ts-ignore a.forEach((value, key) => { if (!b.has(key)) { _failMessage(message, `Property ${path}["${key}"] does not exist on second argument`, path) @@ -285,6 +325,7 @@ const _compare = (a, b, path, message, customCompare) => { if (a.length !== b.length) { _failMessage(message, 'Arrays have a different number of attributes', path) } + // @ts-ignore a.forEach((value, i) => _compare(value, b[i], `${path}[${i}]`, message, customCompare)) break /* istanbul ignore next */ @@ -297,11 +338,27 @@ const _compare = (a, b, path, message, customCompare) => { return true } +/** + * @template T + * @param {T} a + * @param {T} b + * @param {string?} [message] + * @param {function(any,T,T,string,any):boolean} [customCompare] + */ export const compare = (a, b, message = null, customCompare = compareValues) => _compare(a, b, 'obj', message, customCompare) /* istanbul ignore next */ +/** + * @param {boolean} condition + * @param {string?} [message] + * @throws {TestError} + */ export const assert = (condition, message = null) => condition || fail(`Assertion failed${message !== null ? `: ${message}` : ''}`) +/** + * @param {function():void} f + * @throws {TestError} + */ export const fails = f => { let err = null try { @@ -317,7 +374,7 @@ export const fails = f => { } /** - * @param {Object>} tests + * @param {Object>>} tests */ export const runTests = async tests => { const numberOfTests = object.map(tests, mod => object.map(mod, f => /* istanbul ignore next */ f ? 1 : 0).reduce(math.add, 0)).reduce(math.add, 0) diff --git a/testing.test.js b/testing.test.js index ff065da..e752b33 100644 --- a/testing.test.js +++ b/testing.test.js @@ -3,7 +3,10 @@ import * as math from './math.js' import * as buffer from './buffer.js' import * as map from './map.js' -export const testComparing = () => { +/** + * @param {t.TestCase} tc + */ +export const testComparing = tc => { t.compare({}, {}) t.compare({ a: 4 }, { a: 4 }, 'simple compare (object)') t.compare([1, 2], [1, 2], 'simple compare (array)') @@ -30,6 +33,7 @@ export const testComparing = () => { t.compare({ a: 4 }, null, 'childs are not equal') }) t.fails(() => { + // @ts-ignore t.compare({ a: 4 }, [4], 'childs have different types') }) t.fails(() => { diff --git a/time.test.js b/time.test.js index 536d522..6a75c99 100644 --- a/time.test.js +++ b/time.test.js @@ -2,6 +2,9 @@ import * as time from './time.js' import * as t from './testing.js' import * as math from './math.js' +/** + * @param {t.TestCase} tc + */ export const testTime = tc => { const l = time.getDate().getTime() const r = time.getUnixTime() diff --git a/tree.js b/tree.js index dfb3349..a897d40 100644 --- a/tree.js +++ b/tree.js @@ -1,6 +1,7 @@ /** * @module tree */ +// @ts-nocheck TODO: remove or refactor this file const rotate = (tree, parent, newParent, n) => { if (parent === null) { diff --git a/tsconfig.json b/tsconfig.json index 1662f4d..ecb7c58 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -21,7 +21,7 @@ /* Strict Type-Checking Options */ "strict": true, /* Enable all strict type-checking options. */ - "noImplicitAny": false, /* Raise error on expressions and declarations with an implied 'any' type. */ + "noImplicitAny": true, /* Raise error on expressions and declarations with an implied 'any' type. */ // "strictNullChecks": true, /* Enable strict null checks. */ // "strictFunctionTypes": true, /* Enable strict checking of function types. */ // "strictPropertyInitialization": true, /* Enable strict checking of property initialization in classes. */ @@ -40,7 +40,7 @@ // "paths": {}, /* A series of entries which re-map imports to lookup locations relative to the 'baseUrl'. */ // "rootDirs": [], /* List of root folders whose combined content represents the structure of the project at runtime. */ // "typeRoots": [], /* List of folders to include type definitions from. */ - // "types": [], /* Type declaration files to be included in compilation. */ + "types": ["node"], /* Type declaration files to be included in compilation. */ // "allowSyntheticDefaultImports": true, /* Allow default imports from modules with no default export. This does not affect code emit, just typechecking. */ "esModuleInterop": true, /* Enables emit interoperability between CommonJS and ES Modules via creation of namespace objects for all imports. Implies 'allowSyntheticDefaultImports'. */ // "preserveSymlinks": true, /* Do not resolve the real path of symlinks. */