From 1df2ff09ce8f6f91a7ad7a7c94e379bb92ba5954 Mon Sep 17 00:00:00 2001 From: Lukas Tetzlaff Date: Fri, 4 May 2018 07:29:21 +0200 Subject: [PATCH] Improve TS code by adding generics and removing `any` where applicable (#49) --- source/index.ts | 78 +++++++++++++++++++++++--------------------- source/tests/test.ts | 2 +- tsconfig.json | 7 +--- 3 files changed, 42 insertions(+), 45 deletions(-) diff --git a/source/index.ts b/source/index.ts index 18ce4e8..ddb4c6f 100644 --- a/source/index.ts +++ b/source/index.ts @@ -8,6 +8,13 @@ export interface ArrayLike { length: number; } +export interface Class { + new(...args: any[]): T; +} + +type DomElement = object & { nodeType: 1; nodeName: string }; +type NodeStream = object & { pipe: Function }; + export const enum TypeName { null = 'null', boolean = 'boolean', @@ -60,30 +67,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)) { @@ -115,7 +117,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: any): value is object => typeof value === 'object'; // tslint:disable:variable-name export const undefined = isOfType('undefined'); @@ -123,7 +125,7 @@ namespace is { // tslint:disable-line:no-namespace export const number = isOfType('number'); 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 class_ = (value: any): value is Class => function_(value) && value.toString().startsWith('class '); export const boolean = (value: any): value is boolean => value === true || value === false; export const symbol = isOfType('symbol'); // tslint:enable:variable-name @@ -132,32 +134,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: any): value is object => !nullOrUndefined(value) && (function_(value) || isObject(value)); + export const iterable = (value: any): 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: any): value is Promise => + isObjectOfType>(TypeName.Promise)(value); const hasPromiseAPI = (value: any): value is Promise => !null_(value) && - isObject(value) && + isObject(value) as any && function_(value.then) && function_(value.catch); export const promise = (value: any): 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 generatorFunction = isObjectOfType(TypeName.GeneratorFunction); + export const asyncFunction = isObjectOfType(TypeName.AsyncFunction); export const boundFunction = (value: any): 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: any): value is Map => isObjectOfType>(TypeName.Map)(value); + export const set = (value: any): value is Set => isObjectOfType>(TypeName.Set)(value); + export const weakMap = (value: any): value is WeakMap => isObjectOfType>(TypeName.WeakMap)(value); + export const weakSet = (value: any): value is WeakSet => isObjectOfType>(TypeName.WeakSet)(value); export const int8Array = isObjectOfType(TypeName.Int8Array); export const uint8Array = isObjectOfType(TypeName.Uint8Array); @@ -173,7 +175,7 @@ namespace is { // tslint:disable-line:no-namespace export const sharedArrayBuffer = isObjectOfType(TypeName.SharedArrayBuffer); export const dataView = isObjectOfType(TypeName.DataView); - export const directInstanceOf = (instance: any, klass: any) => Object.getPrototypeOf(instance) === klass.prototype; + export const directInstanceOf = (instance: any, klass: Class): instance is T => Object.getPrototypeOf(instance) === klass.prototype; export const truthy = (value: any) => Boolean(value); export const falsy = (value: any) => !value; @@ -247,11 +249,11 @@ namespace is { // tslint:disable-line:no-namespace 'nodeValue' ]; - export const domElement = (value: any) => object(value) && value.nodeType === NODE_TYPE_ELEMENT && string(value.nodeName) && + export const domElement = (value: any): value is DomElement => object(value) as any && value.nodeType === NODE_TYPE_ELEMENT && string(value.nodeName) && !plainObject(value) && DOM_PROPERTIES_TO_CHECK.every(property => property in value); export const observable = (value: any) => Boolean(value && value[symbolObservable] && value === value[symbolObservable]()); - export const nodeStream = (value: any) => !nullOrUndefined(value) && isObject(value) && function_(value.pipe) && !observable(value); + export const nodeStream = (value: any): value is NodeStream => !nullOrUndefined(value) && isObject(value) as any && function_(value.pipe) && !observable(value); export const infinite = (value: any) => value === Infinity || value === -Infinity; diff --git a/source/tests/test.ts b/source/tests/test.ts index 737fd2e..c86ed75 100644 --- a/source/tests/test.ts +++ b/source/tests/test.ts @@ -332,7 +332,7 @@ const types = new Map([ ] }], ['infinite', { - is: m.infinite, + is: m.infinite, fixtures: [ Infinity, -Infinity diff --git a/tsconfig.json b/tsconfig.json index 161f88e..fe251cc 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -14,16 +14,11 @@ "es2017.sharedmemory" ], "stripInternal": true, - "noImplicitAny": true, + "strict": true, "noImplicitReturns": true, - "noImplicitThis": true, "noUnusedLocals": true, "noUnusedParameters": true, "noFallthroughCasesInSwitch": true, - "strictNullChecks": true, - "strictFunctionTypes": true, - "strictPropertyInitialization": true, - "alwaysStrict": true, "esModuleInterop": true }, "exclude": [