From ff99cea8da4a04661097bfa1298f73e0f5466a4e Mon Sep 17 00:00:00 2001 From: Sam Verschueren Date: Fri, 30 Mar 2018 07:52:46 +0200 Subject: [PATCH] Add type guards for better type inference --- source/index.ts | 95 ++++++++++++++++++++++++++----------------------- tsconfig.json | 5 +++ 2 files changed, 56 insertions(+), 44 deletions(-) diff --git a/source/index.ts b/source/index.ts index 39bb591..58d8482 100644 --- a/source/index.ts +++ b/source/index.ts @@ -1,5 +1,12 @@ import * as util from 'util'; +type TypedArray = Int8Array | Uint8Array | Uint8ClampedArray | Int16Array | Uint16Array | Int32Array | Uint32Array | Float32Array | Float64Array; +type Primitive = null | undefined | string | number | boolean | Symbol; + +export interface ArrayLike { + length: number; +} + export const enum TypeName { null = 'null', boolean = 'boolean', @@ -36,7 +43,7 @@ export const enum TypeName { } const toString = Object.prototype.toString; -const isOfType = (type: string) => (value: any) => typeof value === type; // tslint:disable-line:strict-type-predicates +const isOfType = (type: string) => (value: any): value is T => typeof value === type; // tslint:disable-line:strict-type-predicates const getObjectType = (value: any): TypeName | null => { const objectName = toString.call(value).slice(8, -1) as string; @@ -48,7 +55,7 @@ const getObjectType = (value: any): TypeName | null => { return null; }; -const isObjectOfType = (type: TypeName) => (value: any) => getObjectType(value) === type; +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) { @@ -105,60 +112,60 @@ namespace is { // tslint:disable-line:no-namespace const isObject = (value: any) => typeof value === 'object'; // tslint:disable:variable-name - export const undefined = isOfType('undefined'); - export const string = isOfType('string'); - export const number = isOfType('number'); - export const function_ = isOfType('function'); - export const null_ = (value: any) => value === null; + export const undefined = isOfType('undefined'); + export const string = isOfType('string'); + 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 boolean = (value: any) => value === true || value === false; - export const symbol = isOfType('symbol'); + export const boolean = (value: any): value is boolean => value === true || value === false; + export const symbol = isOfType('symbol'); // tslint:enable:variable-name export const array = Array.isArray; export const buffer = Buffer.isBuffer; - export const nullOrUndefined = (value: any) => null_(value) || undefined(value); + 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) => !nullOrUndefined(value) && function_(value[Symbol.iterator]); - export const generator = (value: any) => iterable(value) && function_(value.next) && function_(value.throw); + export const iterable = (value: any): value is Iterator => !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 = isObjectOfType>(TypeName.Promise); - const hasPromiseAPI = (value: any) => + const hasPromiseAPI = (value: any): value is Promise => !null_(value) && isObject(value) && function_(value.then) && function_(value.catch); - export const promise = (value: any) => nativePromise(value) || hasPromiseAPI(value); + 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 boundFunction = (value: any) => function_(value) && !value.hasOwnProperty('prototype'); + 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 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 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 int8Array = isObjectOfType(TypeName.Int8Array); - export const uint8Array = isObjectOfType(TypeName.Uint8Array); - export const uint8ClampedArray = isObjectOfType(TypeName.Uint8ClampedArray); - export const int16Array = isObjectOfType(TypeName.Int16Array); - export const uint16Array = isObjectOfType(TypeName.Uint16Array); - export const int32Array = isObjectOfType(TypeName.Int32Array); - export const uint32Array = isObjectOfType(TypeName.Uint32Array); - export const float32Array = isObjectOfType(TypeName.Float32Array); - export const float64Array = isObjectOfType(TypeName.Float64Array); + export const int8Array = isObjectOfType(TypeName.Int8Array); + export const uint8Array = isObjectOfType(TypeName.Uint8Array); + export const uint8ClampedArray = isObjectOfType(TypeName.Uint8ClampedArray); + export const int16Array = isObjectOfType(TypeName.Int16Array); + export const uint16Array = isObjectOfType(TypeName.Uint16Array); + export const int32Array = isObjectOfType(TypeName.Int32Array); + export const uint32Array = isObjectOfType(TypeName.Uint32Array); + export const float32Array = isObjectOfType(TypeName.Float32Array); + export const float64Array = isObjectOfType(TypeName.Float64Array); - export const arrayBuffer = isObjectOfType(TypeName.ArrayBuffer); - export const sharedArrayBuffer = isObjectOfType(TypeName.SharedArrayBuffer); - export const dataView = isObjectOfType(TypeName.DataView); + export const arrayBuffer = isObjectOfType(TypeName.ArrayBuffer); + export const sharedArrayBuffer = isObjectOfType(TypeName.SharedArrayBuffer); + export const dataView = isObjectOfType(TypeName.DataView); export const directInstanceOf = (instance: any, klass: any) => Object.getPrototypeOf(instance) === klass.prototype; @@ -175,10 +182,10 @@ namespace is { // tslint:disable-line:no-namespace 'symbol' ]); - export const primitive = (value: any) => null_(value) || primitiveTypes.has(typeof value); + export const primitive = (value: any): value is Primitive => null_(value) || primitiveTypes.has(typeof value); - export const integer = (value: any) => Number.isInteger(value); - export const safeInteger = (value: any) => Number.isSafeInteger(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 plainObject = (value: any) => { // From: https://github.com/sindresorhus/is-plain-obj/blob/master/index.js @@ -200,7 +207,7 @@ namespace is { // tslint:disable-line:no-namespace TypeName.Float32Array, TypeName.Float64Array ]); - export const typedArray = (value: any) => { + export const typedArray = (value: any): value is TypedArray => { const objectType = getObjectType(value); if (objectType === null) { @@ -211,11 +218,11 @@ namespace is { // tslint:disable-line:no-namespace }; const isValidLength = (value: any) => safeInteger(value) && value > -1; - export const arrayLike = (value: any) => !nullOrUndefined(value) && !function_(value) && isValidLength(value.length); + export const arrayLike = (value: any): value is ArrayLike => !nullOrUndefined(value) && !function_(value) && isValidLength(value.length); export const inRange = (value: number, range: number | number[]) => { if (number(range)) { - return value >= Math.min(0, range as number) && value <= Math.max(range as number, 0); + return value >= Math.min(0, range) && value <= Math.max(range, 0); } if (array(range) && range.length === 2) { diff --git a/tsconfig.json b/tsconfig.json index 5621df9..afe174b 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -7,6 +7,11 @@ "declaration": true, "pretty": true, "newLine": "lf", + "lib": [ + "dom", + "es2016", + "es2017.sharedmemory" + ], "stripInternal": true, "noImplicitAny": true, "noImplicitReturns": true,