[6.x] TypeScriptify src/utils. (#24638)

This commit is contained in:
Aleh Zasypkin 2018-10-29 12:54:13 +01:00 committed by GitHub
parent e197a30f37
commit f659802d96
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
12 changed files with 101 additions and 180 deletions

View file

@ -17,12 +17,17 @@
* under the License.
*/
export class BinderBase {
constructor() {
this.disposal = [];
}
export interface Emitter {
on: (args: any[]) => void;
off: (args: any[]) => void;
addListener: Emitter['on'];
removeListener: Emitter['off'];
}
on(emitter, ...args) {
export class BinderBase {
private disposal: Array<() => void> = [];
public on(emitter: Emitter, ...args: any[]) {
const on = emitter.on || emitter.addListener;
const off = emitter.off || emitter.removeListener;
@ -30,7 +35,7 @@ export class BinderBase {
this.disposal.push(() => off.apply(emitter, args));
}
destroy() {
public destroy() {
const destroyers = this.disposal;
this.disposal = [];
destroyers.forEach(fn => fn());

View file

@ -17,15 +17,14 @@
* under the License.
*/
import { BinderBase } from './binder';
import { BinderBase, Emitter } from './binder';
export class BinderFor extends BinderBase {
constructor(emitter) {
constructor(private readonly emitter: Emitter) {
super();
this.emitter = emitter;
}
on(...args) {
public on(...args: any[]) {
super.on(this.emitter, ...args);
}
}

View file

@ -17,38 +17,40 @@
* under the License.
*/
import expect from 'expect.js';
import _ from 'lodash';
import { keysToSnakeCaseShallow, keysToCamelCaseShallow } from '../case_conversion';
import { keysToCamelCaseShallow, keysToSnakeCaseShallow } from './case_conversion';
describe('keysToSnakeCaseShallow', function () {
it('should convert all of an object\'s keys to snake case', function () {
describe('keysToSnakeCaseShallow', () => {
it("should convert all of an object's keys to snake case", () => {
const result = keysToSnakeCaseShallow({
camelCase: 'camel_case',
'kebab-case': 'kebab_case',
snake_case: 'snake_case'
snake_case: 'snake_case',
});
_.forEach(result, function (value, key) {
expect(key).to.be(value);
});
expect(result).toMatchInlineSnapshot(`
Object {
"camel_case": "camel_case",
"kebab_case": "kebab_case",
"snake_case": "snake_case",
}
`);
});
});
describe('keysToCamelCaseShallow', function () {
it('should convert all of an object\'s keys to camel case', function () {
describe('keysToCamelCaseShallow', () => {
it("should convert all of an object's keys to camel case", () => {
const result = keysToCamelCaseShallow({
camelCase: 'camelCase',
'kebab-case': 'kebabCase',
snake_case: 'snakeCase'
snake_case: 'snakeCase',
});
_.forEach(result, function (value, key) {
expect(key).to.be(value);
});
expect(result).toMatchInlineSnapshot(`
Object {
"camelCase": "camelCase",
"kebabCase": "kebabCase",
"snakeCase": "snakeCase",
}
`);
});
});

View file

@ -19,13 +19,13 @@
import _ from 'lodash';
export function keysToSnakeCaseShallow(object) {
export function keysToSnakeCaseShallow(object: Record<string, any>) {
return _.mapKeys(object, (value, key) => {
return _.snakeCase(key);
});
}
export function keysToCamelCaseShallow(object) {
export function keysToCamelCaseShallow(object: Record<string, any>) {
return _.mapKeys(object, (value, key) => {
return _.camelCase(key);
});

View file

@ -1,87 +0,0 @@
/*
* Licensed to Elasticsearch B.V. under one or more contributor
* license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright
* ownership. Elasticsearch B.V. licenses this file to you under
* the Apache License, Version 2.0 (the "License"); you may
* not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
const set = Symbol('internal set');
export default class Collection {
constructor() { // Set's have a length of 0, mimic that
this[set] = new Set(arguments[0] || []);
}
/******
** Collection API
******/
toArray() {
return [...this.values()];
}
toJSON() {
return this.toArray();
}
/******
** ES Set Api
******/
static get [Symbol.species]() {
return Collection;
}
get size() {
return this[set].size;
}
add(value) {
return this[set].add(value);
}
clear() {
return this[set].clear();
}
delete(value) {
return this[set].delete(value);
}
entries() {
return this[set].entries();
}
forEach(callbackFn, thisArg) {
return this[set].forEach(callbackFn, thisArg);
}
has(value) {
return this[set].has(value);
}
keys() {
return this[set].keys();
}
values() {
return this[set].values();
}
[Symbol.iterator]() {
return this[set][Symbol.iterator]();
}
}

View file

@ -17,64 +17,63 @@
* under the License.
*/
import { deepCloneWithBuffers } from '../deep_clone_with_buffers';
import expect from 'expect.js';
import { deepCloneWithBuffers } from './deep_clone_with_buffers';
describe('deepCloneWithBuffers()', function () {
it('deep clones objects', function () {
describe('deepCloneWithBuffers()', () => {
it('deep clones objects', () => {
const source = {
a: {
b: {},
c: {},
d: [
{
e: 'f'
}
]
}
e: 'f',
},
],
},
};
const output = deepCloneWithBuffers(source);
expect(source.a).to.eql(output.a);
expect(source.a).to.not.be(output.a);
expect(source.a).toEqual(output.a);
expect(source.a).not.toBe(output.a);
expect(source.a.b).to.eql(output.a.b);
expect(source.a.b).to.not.be(output.a.b);
expect(source.a.b).toEqual(output.a.b);
expect(source.a.b).not.toBe(output.a.b);
expect(source.a.c).to.eql(output.a.c);
expect(source.a.c).to.not.be(output.a.c);
expect(source.a.c).toEqual(output.a.c);
expect(source.a.c).not.toBe(output.a.c);
expect(source.a.d).to.eql(output.a.d);
expect(source.a.d).to.not.be(output.a.d);
expect(source.a.d).toEqual(output.a.d);
expect(source.a.d).not.toBe(output.a.d);
expect(source.a.d[0]).to.eql(output.a.d[0]);
expect(source.a.d[0]).to.not.be(output.a.d[0]);
expect(source.a.d[0]).toEqual(output.a.d[0]);
expect(source.a.d[0]).not.toBe(output.a.d[0]);
});
it('copies buffers but keeps them buffers', function () {
it('copies buffers but keeps them buffers', () => {
const input = new Buffer('i am a teapot', 'utf8');
const output = deepCloneWithBuffers(input);
expect(Buffer.isBuffer(input)).to.be.ok();
expect(Buffer.isBuffer(output)).to.be.ok();
expect(Buffer.isBuffer(input)).toBe(true);
expect(Buffer.isBuffer(output)).toBe(true);
expect(Buffer.compare(output, input));
expect(output).to.not.be(input);
expect(output).not.toBe(input);
});
it('copies buffers that are deep', function () {
it('copies buffers that are deep', () => {
const input = {
a: {
b: {
c: new Buffer('i am a teapot', 'utf8')
}
}
c: new Buffer('i am a teapot', 'utf8'),
},
},
};
const output = deepCloneWithBuffers(input);
expect(Buffer.isBuffer(input.a.b.c)).to.be.ok();
expect(Buffer.isBuffer(output.a.b.c)).to.be.ok();
expect(Buffer.isBuffer(input.a.b.c)).toBe(true);
expect(Buffer.isBuffer(output.a.b.c)).toBe(true);
expect(Buffer.compare(output.a.b.c, input.a.b.c));
expect(output.a.b.c).to.not.be(input.a.b.c);
expect(output.a.b.c).not.toBe(input.a.b.c);
});
});

View file

@ -19,12 +19,15 @@
import { cloneDeep } from 'lodash';
function cloneBuffersCustomizer(val) {
// We should add `any` return type to overcome bug in lodash types, customizer
// in lodash 3.* can return `undefined` if cloning is handled by the lodash, but
// type of the customizer function doesn't expect that.
function cloneBuffersCustomizer(val: unknown): any {
if (Buffer.isBuffer(val)) {
return new Buffer(val);
}
}
export function deepCloneWithBuffers(vals) {
return cloneDeep(vals, cloneBuffersCustomizer);
export function deepCloneWithBuffers<T>(val: T): T {
return cloneDeep(val, cloneBuffersCustomizer);
}

View file

@ -28,11 +28,11 @@
* sub-delims = "!" / "$" / "&" / "'" / "(" / ")"
* / "*" / "+" / "," / ";" / "="
*/
export function encodeQueryComponent(val, pctEncodeSpaces = false) {
export function encodeQueryComponent(val: string, pctEncodeSpaces = false) {
return encodeURIComponent(val)
.replace(/%40/gi, '@')
.replace(/%3A/gi, ':')
.replace(/%24/g, '$')
.replace(/%2C/gi, ',')
.replace(/%20/g, (pctEncodeSpaces ? '%20' : '+'));
.replace(/%20/g, pctEncodeSpaces ? '%20' : '+');
}

View file

@ -17,9 +17,9 @@
* under the License.
*/
import { pkg } from './package_json';
import { resolve } from 'path';
import { pkg } from './package_json';
export function fromRoot(...args) {
export function fromRoot(...args: string[]) {
return resolve(pkg.__dirname, ...args);
}

View file

@ -17,29 +17,30 @@
* under the License.
*/
import expect from 'expect.js';
import { getFlattenedObject } from '../get_flattened_object';
import { getFlattenedObject } from './get_flattened_object';
describe('getFlattenedObject()', () => {
it('throws when rootValue is not an object or is an array', () => {
expect(() => getFlattenedObject(1)).to.throwError('number');
expect(() => getFlattenedObject(Infinity)).to.throwError('number');
expect(() => getFlattenedObject(NaN)).to.throwError('number');
expect(() => getFlattenedObject(false)).to.throwError('boolean');
expect(() => getFlattenedObject(null)).to.throwError('null');
expect(() => getFlattenedObject(undefined)).to.throwError('undefined');
expect(() => getFlattenedObject([])).to.throwError('array');
expect(() => getFlattenedObject(1)).toThrowError();
expect(() => getFlattenedObject(Infinity)).toThrowError();
expect(() => getFlattenedObject(NaN)).toThrowError();
expect(() => getFlattenedObject(false)).toThrowError();
expect(() => getFlattenedObject(null)).toThrowError();
expect(() => getFlattenedObject(undefined)).toThrowError();
expect(() => getFlattenedObject([])).toThrowError();
});
it('flattens objects', () => {
expect(getFlattenedObject({ a: 'b' })).to.eql({ a: 'b' });
expect(getFlattenedObject({ a: { b: 'c' } })).to.eql({ 'a.b': 'c' });
expect(getFlattenedObject({ a: { b: 'c' }, d: { e: 'f' } })).to.eql({ 'a.b': 'c', 'd.e': 'f' });
expect(getFlattenedObject({ a: 'b' })).toEqual({ a: 'b' });
expect(getFlattenedObject({ a: { b: 'c' } })).toEqual({ 'a.b': 'c' });
expect(getFlattenedObject({ a: { b: 'c' }, d: { e: 'f' } })).toEqual({
'a.b': 'c',
'd.e': 'f',
});
});
it('does not flatten arrays', () => {
expect(getFlattenedObject({ a: ['b'] })).to.eql({ a: ['b'] });
expect(getFlattenedObject({ a: { b: ['c', 'd'] } })).to.eql({ 'a.b': ['c', 'd'] });
expect(getFlattenedObject({ a: ['b'] })).toEqual({ a: ['b'] });
expect(getFlattenedObject({ a: { b: ['c', 'd'] } })).toEqual({ 'a.b': ['c', 'd'] });
});
});

View file

@ -17,8 +17,7 @@
* under the License.
*/
function shouldReadKeys(value) {
function shouldReadKeys(value: unknown): value is Record<string, any> {
return typeof value === 'object' && value !== null && !Array.isArray(value);
}
@ -34,21 +33,21 @@ function shouldReadKeys(value) {
* @param {Object} rootValue
* @returns {Object}
*/
export function getFlattenedObject(rootValue) {
export function getFlattenedObject(rootValue: unknown) {
if (!shouldReadKeys(rootValue)) {
throw new TypeError(`Root value is not flatten-able, received ${rootValue}`);
}
return (function flatten(acc, prefix, object) {
return Object.keys(object).reduce((acc, key) => {
const value = object[key];
return (function flatten<T extends Record<string, any>>(acc: T, prefix: string, object: T): T {
for (const [key, value] of Object.entries(object)) {
const path = prefix ? `${prefix}.${key}` : key;
if (shouldReadKeys(value)) {
return flatten(acc, path, value);
flatten(acc, path, value);
} else {
return { ...acc, [path]: value };
acc[path] = value;
}
}, acc);
}({}, '', rootValue));
}
return acc;
})({}, '', rootValue);
}