From 8c1e00149c70b34b7a17f915b82d62664fefda06 Mon Sep 17 00:00:00 2001 From: Connor Peet Date: Mon, 14 Sep 2020 11:46:31 -0700 Subject: [PATCH] refactor: remove all usages of any --- .gitignore | 3 +- .npmignore | 5 +- package-lock.json | 17 +++- package.json | 13 +-- src/{test.ts => uts.test.ts} | 0 src/uts.ts | 163 +++++++++++++++++++++-------------- 6 files changed, 123 insertions(+), 78 deletions(-) rename src/{test.ts => uts.test.ts} (100%) diff --git a/.gitignore b/.gitignore index fb0407b..b3b8e35 100644 --- a/.gitignore +++ b/.gitignore @@ -42,5 +42,4 @@ jspm_packages # Optional REPL history .node_repl_history -/uts.js -/uts.d.ts +/dist diff --git a/.npmignore b/.npmignore index 3940cdd..7e7a454 100644 --- a/.npmignore +++ b/.npmignore @@ -1,3 +1,2 @@ -/tsconfig.json -/test.js -/uts.ts +/* +!/dist diff --git a/package-lock.json b/package-lock.json index 424cd50..52f5066 100644 --- a/package-lock.json +++ b/package-lock.json @@ -986,6 +986,17 @@ "flatted": "^2.0.0", "rimraf": "2.6.3", "write": "1.0.3" + }, + "dependencies": { + "rimraf": { + "version": "2.6.3", + "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.6.3.tgz", + "integrity": "sha512-mwqeW5XsA2qAejG46gYdENaxXjx9onRNCfn7L0duuP4hCuTIi/QO7PDK07KJfp1d+izWPrzEJDcSqBa0OZQriA==", + "dev": true, + "requires": { + "glob": "^7.1.3" + } + } } }, "flatted": { @@ -1675,9 +1686,9 @@ "dev": true }, "rimraf": { - "version": "2.6.3", - "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.6.3.tgz", - "integrity": "sha512-mwqeW5XsA2qAejG46gYdENaxXjx9onRNCfn7L0duuP4hCuTIi/QO7PDK07KJfp1d+izWPrzEJDcSqBa0OZQriA==", + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz", + "integrity": "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==", "dev": true, "requires": { "glob": "^7.1.3" diff --git a/package.json b/package.json index 9328941..4049e04 100644 --- a/package.json +++ b/package.json @@ -2,9 +2,11 @@ "name": "uts", "version": "1.1.3", "description": "Microscopic time series database for Node and the browser", - "main": "uts.js", - "typings": "uts.d.ts", + "main": "dist/uts.js", "scripts": { + "prepublishOnly": "npm run compile", + "compile": "rimraf dist && tsc", + "watch": "rimraf dist && tsc --watch", "test": "npm run test:unit && npm run test:lint && npm run test:fmt", "test:unit": "mocha \"dist/**/*.test.js\"", "test:lint": "eslint \"src/**/*.ts\"", @@ -15,7 +17,7 @@ }, "repository": { "type": "git", - "url": "git+https://github.com/WatchBeam/uts.git" + "url": "git+https://github.com/microsoft/uts.git" }, "keywords": [ "time", @@ -26,7 +28,7 @@ "author": "Connor Peet ", "license": "MIT", "bugs": { - "url": "https://github.com/WatchBeam/uts/issues" + "url": "https://github.com/microsoft/uts/issues" }, "prettier": { "trailingComma": "all", @@ -35,7 +37,7 @@ "tabWidth": 2, "arrowParens": "avoid" }, - "homepage": "https://github.com/WatchBeam/uts#readme", + "homepage": "https://github.com/microsoft/uts#readme", "devDependencies": { "@types/chai": "^4.2.12", "@types/mocha": "^8.0.3", @@ -47,6 +49,7 @@ "eslint-plugin-header": "^3.1.0", "mocha": "^8.1.3", "prettier": "^2.1.1", + "rimraf": "^3.0.2", "sinon": "^9.0.3", "typescript": "^4.0.2" } diff --git a/src/test.ts b/src/uts.test.ts similarity index 100% rename from src/test.ts rename to src/uts.test.ts diff --git a/src/uts.ts b/src/uts.ts index 52d4349..69983be 100644 --- a/src/uts.ts +++ b/src/uts.ts @@ -2,13 +2,17 @@ * Copyright (C) Microsoft Corporation. All rights reserved. *--------------------------------------------------------*/ +type InstantiatedMetrics = { + [K in keyof TMetrics]: ReturnType; +}; + /** * The Bin is used internally in the query system and holds analysis for * a group. */ -export class Bin { - private columns: string[]; - private metrics: { [column: string]: Aggregate } = {}; +export class Bin { + private columns: (keyof TMetrics)[]; + private metrics: InstantiatedMetrics; private _size = 0; /** @@ -17,11 +21,16 @@ export class Bin { * @param {Object} group arbitrary object describing the grouping of * the contained metrics. */ - constructor(metrics: { [column: string]: () => Aggregate }, private group?: any) { + constructor(metrics: TMetrics, private readonly group: TGroup) { this.columns = Object.keys(metrics); - this.columns.forEach(col => { - this.metrics[col] = metrics[col](); - }); + + const instantiated: Partial> = {}; + for (const col of this.columns) { + // eslint-disable-next-line @typescript-eslint/no-explicit-any + instantiated[col] = metrics[col]() as any; + } + + this.metrics = instantiated as InstantiatedMetrics; } /** @@ -52,16 +61,17 @@ export class Bin { * Serializes the bin to a results object. * @return {Object} */ - toObject(): BinResult { - const out: BinResult = { results: {} }; + toObject(): BinResult { + // eslint-disable-next-line @typescript-eslint/no-explicit-any + const out: any = { results: {} }; - if (this.group) { + if (this.group !== undefined) { out.group = this.group; } - this.columns.forEach(col => { + for (const col of this.columns) { out.results[col] = this.metrics[col].serialize(); - }); + } return out; } @@ -70,7 +80,7 @@ export class Bin { /** * Parent class of groupers. */ -export abstract class Group { +export abstract class Group { protected _where: { [col: string]: Comparator | Comparator[] } = {}; /** @@ -95,13 +105,21 @@ export abstract class Group { /** * Returns a list of bins with points added to them. */ - abstract binify(data: Point[], metrics: { [column: string]: () => Aggregate }): Bin[]; + abstract binify( + data: Point[], + metrics: TMetrics, + ): Bin[]; +} + +export interface IIntervalGroup { + start: number; + width: number; } /** * The IntervalGrouper groups data based on time. */ -class IntervalGrouper extends Group { +class IntervalGrouper extends Group { /** * @param {Number} interval the size, in milliseconds, of each group bin * @param {Boolean} fill whether to zero-fill bins that don't have data @@ -110,7 +128,10 @@ class IntervalGrouper extends Group { super(); } - binify(data: Point[], metrics: { [column: string]: () => Aggregate }): Bin[] { + binify( + data: Point[], + metrics: TMetrics, + ): Bin[] { let start: number; const timeBound = this.getWhere('time').find(time => time.is === '>'); if (timeBound) { @@ -122,7 +143,7 @@ class IntervalGrouper extends Group { const { interval, now } = this; const count = Math.floor((now - start) / interval) + 1; - let bins = new Array(); + let bins = new Array>(); for (let i = 0; i < count; i++) { bins.push( new Bin(metrics, { @@ -153,9 +174,12 @@ class IntervalGrouper extends Group { * The AnyGrouper is the "base" grouper that just shoves all the data into * a single bin. */ -class AnyGrouper extends Group { - binify(data: Point[], metrics: { [column: string]: () => Aggregate }): Bin[] { - const bin = new Bin(metrics); +class AnyGrouper extends Group { + binify( + data: Point[], + metrics: TMetrics, + ): Bin[] { + const bin = new Bin(metrics, undefined); for (let i = 0; i < data.length; i++) { bin.push(data[i]); } @@ -165,7 +189,7 @@ class AnyGrouper extends Group { } export type PointData = { - [column: string]: any; + [column: string]: number; }; /** @@ -175,8 +199,8 @@ export type PointData = { export class Point { /** * Creates a new point for insertion into a time series. - * @param {Object} data - * @param {[Number]} time insertion time in milliseconds + * @param data + * @param time insertion time in milliseconds */ constructor(private data: PointData, time = Date.now()) { this.data['time'] = time; @@ -186,9 +210,9 @@ export class Point { * Gets a property from the point, returning the default value if * it doens't exist. */ - public get(prop: string): T | undefined; - public get(prop: string, defaultValue: T): T; - public get(prop: string, defaultValue?: T): T { + public get(prop: string): number | undefined; + public get(prop: string, defaultValue: number): number; + public get(prop: string, defaultValue?: number): number | undefined { return this.has(prop) ? this.data[prop] : defaultValue; } @@ -225,17 +249,19 @@ export type BinaryOperator = '>' | '<' | '='; */ export interface Comparator { is: BinaryOperator; - than: any; + than: number; } /** * BinResults are returned from time series queries. */ -export interface BinResult { - group?: any; - results: { [column: string]: any }; +export interface BinResult { + group?: TGroup; + results: { [K in keyof TMetrics]: TMetrics[K] extends () => Aggregate ? R : unknown }; } +type TMetricMin = { [col: string]: () => Aggregate }; + /** * Series contains a logical grouping of results that can be aggregated. */ @@ -273,7 +299,7 @@ export class Series { * Inserts new data into the series at the specified time, * defaulting to the current time if not provided. */ - public insert(data: any, time?: number) { + public insert(data: Point | PointData, time?: number) { this.data.push(data instanceof Point ? data : new Point(data, time)); return this; } @@ -297,7 +323,7 @@ export class Series { const value = comp.than; return (pt: Point): boolean => { - const v = pt.get(col); + const v = pt.get(col); if (v === undefined) { return false; } @@ -369,16 +395,15 @@ export class Series { * // ... * }] */ - public query(options: { - metrics: { [col: string]: () => Aggregate }; + public query(options: { + metrics: TMetrics; where?: { [col: string]: Comparator | Comparator[] }; - group?: Group; - }): BinResult[] { + group?: Group; + }): BinResult[] { options.where = options.where || {}; - options.group = options.group || new AnyGrouper(); + options.group = options.group || ((new AnyGrouper() as unknown) as Group); const data = this.data.filter(this.buildComparator(options.where)); - return options.group .where(options.where) .binify(data, options.metrics) @@ -394,7 +419,7 @@ export class Series { } } -export interface Aggregate { +export interface Aggregate { /** * Adds a new point to the aggregate. */ @@ -403,33 +428,33 @@ export interface Aggregate { /** * Returns the aggregate's result for returning in the query results. */ - serialize(): any; + serialize(): R; } -class Mapper implements Aggregate { - private data = new Array(); +class Mapper implements Aggregate { + private readonly data: R[] = []; /** * Mapper returns the results of a mapping function on the points. */ - constructor(private fn: (pt: Point) => any) {} + constructor(private fn: (pt: Point) => R) {} public push(pt: Point) { this.data.push(this.fn(pt)); } - public serialize() { + public serialize(): R[] { return this.data; } } -class Reducer implements Aggregate { - private result: T; +class Reducer implements Aggregate { + private result: R; /** * Mapper returns the results of a mapping function on the points. */ - constructor(private fn: (current: T, pt: Point) => T, initial: T) { + constructor(private fn: (current: R, pt: Point) => R, initial: R) { this.result = initial; } @@ -442,7 +467,7 @@ class Reducer implements Aggregate { } } -class Average implements Aggregate { +class Average implements Aggregate { private sum = 0; private count = 0; @@ -453,7 +478,7 @@ class Average implements Aggregate { public push(pt: Point) { if (pt.has(this.column)) { - this.sum += pt.get(this.column, 0); + this.sum += pt.get(this.column, 0); this.count += 1; } } @@ -463,7 +488,7 @@ class Average implements Aggregate { } } -class Derivative implements Aggregate { +class Derivative implements Aggregate { private lastChange = 0; private lastValue?: number; private lastTime?: number; @@ -477,8 +502,8 @@ class Derivative implements Aggregate { constructor(private column: string, private interval: number) {} public push(pt: Point) { - const value = pt.get(this.column); - if (!value) { + const value = pt.get(this.column); + if (value === undefined) { return; } @@ -565,15 +590,19 @@ export class TSDB { * returning the mapping results. If the `mapper` is a string, it'll * extract the specified column from the results, lodash style. */ - public static map(mapper: string | ((pt: Point) => any)): () => Aggregate { - let fn: (pt: Point) => any; + public static map(mapper: string): () => Aggregate<(number | undefined)[]>; + public static map(mapper: (pt: Point) => T): () => Aggregate; + public static map( + mapper: string | ((pt: Point) => T), + ): () => Aggregate<(T | undefined)[]> { + let fn: (pt: Point) => T | number | undefined; if (typeof mapper === 'function') { fn = mapper; } else { fn = pt => pt.get(mapper); } - return () => new Mapper(fn); + return () => new Mapper(fn) as Aggregate<(T | undefined)[]>; } /** @@ -581,28 +610,28 @@ export class TSDB { * returning the mapping results. If the `mapper` is a string, it'll * extract the specified column from the results, lodash style. */ - public static reduce(fn: (current: T, pt: Point) => T, initial: T): () => Aggregate { + public static reduce(fn: (current: T, pt: Point) => T, initial: T): () => Aggregate { return () => new Reducer(fn, initial); } /** * Creates a mean metric analysis passed into Series.Query */ - public static mean(column: string): () => Aggregate { + public static mean(column: string): () => Aggregate { return () => new Average(column); } /** * Creates a max metric analysis passed into Series.Query */ - public static max(column: string): () => Aggregate { + public static max(column: string): () => Aggregate { return this.reduce((max, pt) => Math.max(pt.get(column, -Infinity), max), 0); } /** * Creates a min metric analysis passed into Series.Query */ - public static min(column: string): () => Aggregate { + public static min(column: string): () => Aggregate { return this.reduce((min, pt) => Math.min(pt.get(column, Infinity), min), 0); } @@ -610,7 +639,7 @@ export class TSDB { * Creates an analysis which returns points that plot changes in a column, * within a specified interval. */ - public static derivative(column: string, interval: number): () => Aggregate { + public static derivative(column: string, interval: number): () => Aggregate { return () => new Derivative(column, interval); } @@ -618,14 +647,14 @@ export class TSDB { * Creates an analysis that gets the most recent * value of the specified column. */ - public static last(column: string): () => Aggregate { - return this.reduce((x, pt) => pt.get(column, null), null); + public static last(column: string): () => Aggregate { + return this.reduce((_, pt) => pt.get(column), undefined); } /** * Creates a sum metric analysis passed into Series.Query */ - public static sum(column: string): () => Aggregate { + public static sum(column: string): () => Aggregate { return this.reduce((sum, pt) => sum + pt.get(column, 0), 0); } @@ -634,7 +663,7 @@ export class TSDB { * all columns, or you can pass a column to only count points that have * that column. */ - public static count(column = '*'): () => Aggregate { + public static count(column = '*'): () => Aggregate { return this.reduce((sum, pt) => { if (column === '*' || pt.get(column) !== undefined) { return sum + 1; @@ -646,7 +675,11 @@ export class TSDB { /** * Creates a new grouper based on time intervals. */ - public static interval(interval: number, fill = true, now: number = Date.now()): Group { + public static interval( + interval: number, + fill = true, + now: number = Date.now(), + ): Group { return new IntervalGrouper(interval, fill, now); } }