From 09a3f4d5571658cb09cebd22502566b3d62ecf2a Mon Sep 17 00:00:00 2001 From: Lukas Tetzlaff Date: Sun, 29 Apr 2018 19:45:24 +0200 Subject: [PATCH] Improve TS code by adding generics and removing `any` where applicable --- source/index.ts | 97 +++++++++++++++++++++----------------------- source/tests/test.ts | 30 +++++++------- 2 files changed, 61 insertions(+), 66 deletions(-) diff --git a/source/index.ts b/source/index.ts index 64f5600..9da8ee6 100644 --- a/source/index.ts +++ b/source/index.ts @@ -58,30 +58,25 @@ const getObjectType = (value: any): TypeName | null => { const isObjectOfType = (type: TypeName) => (value: any): value is T => getObjectType(value) === type; function is(value: any): TypeName { // tslint:disable-line:only-arrow-functions - if (value === null) { - return TypeName.null; + switch (value) { + case null: + return TypeName.null; + case true: + case false: + return TypeName.boolean; + default: } - if (value === true || value === false) { - return TypeName.boolean; - } - - const type = typeof value; - - if (type === 'undefined') { - return TypeName.undefined; - } - - if (type === 'string') { - return TypeName.string; - } - - if (type === 'number') { - return TypeName.number; - } - - if (type === 'symbol') { - return TypeName.symbol; + switch (typeof value) { + case 'undefined': + return TypeName.undefined; + case 'string': + return TypeName.string; + case 'number': + return TypeName.number; + case 'symbol': + return TypeName.symbol; + default: } if (is.function_(value)) { @@ -109,7 +104,7 @@ function is(value: any): TypeName { // tslint:disable-line:only-arrow-functions } namespace is { // tslint:disable-line:no-namespace - const isObject = (value: any) => typeof value === 'object'; + const isObject = (value: object) => typeof value === 'object'; // tslint:disable-line:strict-type-predicates // tslint:disable:variable-name export const undefined = isOfType('undefined'); @@ -118,7 +113,7 @@ namespace is { // tslint:disable-line:no-namespace export const function_ = isOfType('function'); export const null_ = (value: any): value is null => value === null; export const class_ = (value: any) => function_(value) && value.toString().startsWith('class '); - export const boolean = (value: any): value is boolean => value === true || value === false; + export const boolean = (value: boolean): value is boolean => value === true || value === false; export const symbol = isOfType('symbol'); // tslint:enable:variable-name @@ -126,32 +121,32 @@ namespace is { // tslint:disable-line:no-namespace export const buffer = Buffer.isBuffer; export const nullOrUndefined = (value: any): value is null | undefined => null_(value) || undefined(value); - export const object = (value: any) => !nullOrUndefined(value) && (function_(value) || isObject(value)); - export const iterable = (value: any): value is Iterator => !nullOrUndefined(value) && function_(value[Symbol.iterator]); + export const object = (value: object) => !nullOrUndefined(value) && (function_(value) || isObject(value)); + export const iterable = (value: IterableIterator): value is IterableIterator => !nullOrUndefined(value) && function_(value[Symbol.iterator]); export const generator = (value: any): value is Generator => iterable(value) && function_(value.next) && function_(value.throw); - export const nativePromise = isObjectOfType>(TypeName.Promise); + export const nativePromise = (value: Promise): value is Promise => + isObjectOfType>(TypeName.Promise)(value); - const hasPromiseAPI = (value: any): value is Promise => + const hasPromiseAPI = (value: Promise): value is Promise => !null_(value) && isObject(value) && function_(value.then) && function_(value.catch); - export const promise = (value: any): value is Promise => nativePromise(value) || hasPromiseAPI(value); + export const promise = (value: Promise): value is Promise => nativePromise(value) || hasPromiseAPI(value); - const isFunctionOfType = (type: TypeName) => isObjectOfType(type); - export const generatorFunction = isFunctionOfType(TypeName.GeneratorFunction); - export const asyncFunction = isFunctionOfType(TypeName.AsyncFunction); - export const boundFunction = (value: any): value is Function => function_(value) && !value.hasOwnProperty('prototype'); + export const generatorFunction = isObjectOfType(TypeName.GeneratorFunction); + export const asyncFunction = isObjectOfType(TypeName.AsyncFunction); + export const boundFunction = (value: Function): value is Function => function_(value) && !value.hasOwnProperty('prototype'); export const regExp = isObjectOfType(TypeName.RegExp); export const date = isObjectOfType(TypeName.Date); export const error = isObjectOfType(TypeName.Error); - export const map = isObjectOfType>(TypeName.Map); - export const set = isObjectOfType>(TypeName.Set); - export const weakMap = isObjectOfType>(TypeName.WeakMap); - export const weakSet = isObjectOfType>(TypeName.WeakSet); + export const map = (value: Map): value is Map => isObjectOfType>(TypeName.Map)(value); + export const set = (value: Set): value is Set => isObjectOfType>(TypeName.Set)(value); + export const weakMap = (value: WeakMap): value is WeakMap => isObjectOfType>(TypeName.WeakMap)(value); + export const weakSet = (value: WeakSet): value is WeakSet => isObjectOfType>(TypeName.WeakSet)(value); export const int8Array = isObjectOfType(TypeName.Int8Array); export const uint8Array = isObjectOfType(TypeName.Uint8Array); @@ -172,7 +167,7 @@ namespace is { // tslint:disable-line:no-namespace export const truthy = (value: any) => Boolean(value); export const falsy = (value: any) => !value; - export const nan = (value: any) => Number.isNaN(value); + export const nan = (value: number) => Number.isNaN(value); const primitiveTypes = new Set([ 'undefined', @@ -182,10 +177,10 @@ namespace is { // tslint:disable-line:no-namespace 'symbol' ]); - export const primitive = (value: any): value is Primitive => null_(value) || primitiveTypes.has(typeof value); + export const primitive = (value: Primitive): value is Primitive => null_(value) || primitiveTypes.has(typeof value); - export const integer = (value: any): value is number => Number.isInteger(value); - export const safeInteger = (value: any): value is number => Number.isSafeInteger(value); + export const integer = (value: number): value is number => Number.isInteger(value); + export const safeInteger = (value: number): value is number => Number.isSafeInteger(value); export const plainObject = (value: any) => { // From: https://github.com/sindresorhus/is-plain-obj/blob/master/index.js @@ -207,7 +202,7 @@ namespace is { // tslint:disable-line:no-namespace TypeName.Float32Array, TypeName.Float64Array ]); - export const typedArray = (value: any): value is TypedArray => { + export const typedArray = (value: TypedArray): value is TypedArray => { const objectType = getObjectType(value); if (objectType === null) { @@ -217,8 +212,8 @@ namespace is { // tslint:disable-line:no-namespace return typedArrayTypes.has(objectType); }; - const isValidLength = (value: any) => safeInteger(value) && value > -1; - export const arrayLike = (value: any): value is ArrayLike => !nullOrUndefined(value) && !function_(value) && isValidLength(value.length); + const isValidLength = (value: number) => safeInteger(value) && value > -1; + export const arrayLike = (value: ArrayLike): value is ArrayLike => !nullOrUndefined(value) && !function_(value) && isValidLength(value.length); export const inRange = (value: number, range: number | number[]) => { if (number(range)) { @@ -246,22 +241,22 @@ namespace is { // tslint:disable-line:no-namespace export const nodeStream = (value: any) => !nullOrUndefined(value) && isObject(value) && function_(value.pipe); - export const infinite = (value: any) => value === Infinity || value === -Infinity; + export const infinite = (value: number) => value === Infinity || value === -Infinity; const isAbsoluteMod2 = (value: number) => (rem: number) => integer(rem) && Math.abs(rem % 2) === value; export const even = isAbsoluteMod2(0); export const odd = isAbsoluteMod2(1); - const isWhiteSpaceString = (value: any) => string(value) && /\S/.test(value) === false; - const isEmptyStringOrArray = (value: any) => (string(value) || array(value)) && value.length === 0; + const isWhiteSpaceString = (value: string) => string(value) && /\S/.test(value) === false; + const isEmptyStringOrArray = (value: string | any[]) => (string(value) || array(value)) && value.length === 0; const isEmptyObject = (value: any) => !map(value) && !set(value) && object(value) && Object.keys(value).length === 0; const isEmptyMapOrSet = (value: any) => (map(value) || set(value)) && value.size === 0; export const empty = (value: any) => falsy(value) || isEmptyStringOrArray(value) || isEmptyObject(value) || isEmptyMapOrSet(value); export const emptyOrWhitespace = (value: any) => empty(value) || isWhiteSpaceString(value); - type ArrayMethod = (fn: (value: any, index: number, array: any[]) => boolean, thisArg?: any) => boolean; - const predicateOnArray = (method: ArrayMethod, predicate: any, values: any[]) => { + type ArrayMethod = (fn: (value: T, index: number, array: T[]) => boolean, thisArg?: any) => boolean; + const predicateOnArray = (method: ArrayMethod, predicate: T, values: any[]) => { if (function_(predicate) === false) { throw new TypeError(`Invalid predicate: ${util.inspect(predicate)}`); } @@ -274,8 +269,8 @@ namespace is { // tslint:disable-line:no-namespace }; // tslint:disable variable-name - export const any = (predicate: any, ...values: any[]) => predicateOnArray(Array.prototype.some, predicate, values); - export const all = (predicate: any, ...values: any[]) => predicateOnArray(Array.prototype.every, predicate, values); + export const any = (predicate: T, ...values: any[]) => predicateOnArray(Array.prototype.some, predicate, values); + export const all = (predicate: T, ...values: any[]) => predicateOnArray(Array.prototype.every, predicate, values); // tslint:enable variable-name } diff --git a/source/tests/test.ts b/source/tests/test.ts index 64edaed..42bf32e 100644 --- a/source/tests/test.ts +++ b/source/tests/test.ts @@ -322,7 +322,7 @@ const types = new Map([ ] }], ['infinite', { - is: m.infinite, + is: m.infinite, fixtures: [ Infinity, -Infinity @@ -582,15 +582,15 @@ test('is.plainObject', t => { }); test('is.iterable', t => { - t.true(m.iterable('')); - t.true(m.iterable([])); - t.true(m.iterable(new Map())); - t.false(m.iterable(null)); - t.false(m.iterable(undefined)); - t.false(m.iterable(0)); - t.false(m.iterable(NaN)); - t.false(m.iterable(Infinity)); - t.false(m.iterable({})); + t.true(m.iterable('' as any)); + t.true(m.iterable([] as any)); + t.true(m.iterable(new Map() as any)); + t.false(m.iterable(null as any)); + t.false(m.iterable(undefined as any)); + t.false(m.iterable(0 as any)); + t.false(m.iterable(NaN as any)); + t.false(m.iterable(Infinity as any)); + t.false(m.iterable({} as any)); }); test('is.class', t => { @@ -623,9 +623,9 @@ test('is.typedArray', t => { t.true(m.typedArray(el)); } - t.false(m.typedArray(new ArrayBuffer(1))); - t.false(m.typedArray([])); - t.false(m.typedArray({})); + t.false(m.typedArray(new ArrayBuffer(1) as any)); + t.false(m.typedArray([] as any)); + t.false(m.typedArray({} as any)); }); test('is.arrayLike', t => { @@ -635,9 +635,9 @@ test('is.arrayLike', t => { t.true(m.arrayLike([])); t.true(m.arrayLike('unicorn')); - t.false(m.arrayLike({})); + t.false(m.arrayLike({} as any)); t.false(m.arrayLike(() => {})); // tslint:disable-line:no-empty - t.false(m.arrayLike(new Map())); + t.false(m.arrayLike(new Map() as any)); }); test('is.inRange', t => {