import { isDate as isDatePredicate } from 'date-fns' import { Refinement } from 'fp-ts/lib/function' import { Falsy, Nil, Undefinedable } from './typeLevel' /** * FP-TS tag */ export type TaggedObject = A & { _tag: string } /** * FP-TS data structures are tagged objects * but the tag isn't part of the type signature * this helps identify those objects. */ export const isTaggedObject = (x: unknown): x is TaggedObject => isObject(x) && '_tag' in x export type GuardedType = G extends Refinement ? T : never /** * Will throw a type error if switch cases aren't exhaustive. */ export const assertNever = (x: never): never => { throw new Error('assertNever check failed. Please check call site.') } /** Ensures all actions in reducer are handled in switch statement */ export const endReducer = (state: T, _action: never): T => { return state } /** * Tests whether or not an argument is null (type guard) */ export const isNull = (x: unknown): x is null => x === null /** * Tests whether or not an argument is undefined (type guard) */ export const isUndefined = (x: unknown): x is undefined => x === undefined /** * Tests whether or not an argument is not undefined (type guard) */ export const isNotUndefined = ( x: Undefinedable, ): x is Exclude => !isUndefined(x) /** * Tests whether or not an argument is null or undefined (type guard) */ export const isNil = (x: unknown): x is Nil => isNull(x) || isUndefined(x) /** * Tests whether or not an argument is null or undefined (type guard) */ export const isNotNil = (x: T | Nil): x is T => !isNil(x) export const isBoolean = (x: unknown): x is boolean => x === true || x === false /** * Type guard for the `false` literal of the `boolean` primitive */ export const isFalse = (x: unknown): x is false => typeof x === 'boolean' && x === false /** * Type guard for the `true` literal of the `boolean` primitive */ export const isTrue = (x: unknown): x is true => typeof x === 'boolean' && x === true /** * Type guard for the `0` literal of the `number` primitive */ export const isZero = (x: unknown): x is 0 => isNumber(x) && x === 0 /** * Type guard for the Falsy type */ export const isFalsy = (x: unknown): x is Falsy => isNil(x) || isFalse(x) || isEmptyString(x) || isZero(x) || (isNumber(x) && Number.isNaN(x)) /** * Type guard for the `string` primitive */ export const isString = (x: unknown): x is string => typeof x === 'string' /** * * @param x Type guard for not the `string` primitive */ export const isNotString = (x: T): x is Exclude => typeof x !== 'string' /** * Type guard for the `''` literal of the `string` primitive */ export const isEmptyString = (x: unknown): x is '' => isString(x) && x === '' /** * Type guard for `string` primitives that are not `''` */ export const isNonEmptyString = (x: unknown): x is string => isString(x) && !isEmptyString(x) /** * Type guard for the `number` primitive */ export const isNumber = (x: unknown): x is number => typeof x === 'number' /** * The opposite of isNaN (built-in) */ export const isNotNaN = (x: unknown): x is number => typeof x === 'number' && !isNaN(x) /** * Type guard for finite `number` primitive * false for NaN, -Infinity, Infinity */ export const isFinite = (x: unknown): x is number => typeof x === 'number' && Number.isFinite(x) /** * Type guard for the `object` type */ export const isObject = (x: unknown): x is object => !isNull(x) && typeof x === 'object' && x instanceof Object /** * Type guard for the `Date` type */ export const isDate = (x: unknown): x is Date => isDatePredicate(x) /** * Type guard for the `Function` type */ // eslint-disable-next-line @typescript-eslint/ban-types export const isFunction = (x: unknown): x is Function => x instanceof Function /** * Type guard for the `Array` type */ export const isArray = (as: Array | unknown): as is Array => Array.isArray(as) /** * Type guard for the `Array` type with `.length > 0` * NOTE: this is *not* an fp-ts NonEmptyArray */ export const isNonEmptyArray = (as: Array | unknown): as is Array => isArray(as) && as.length > 0 /** * Type guard for the `Array` type */ export const isBooleanArray = (as: unknown): as is Array => isArray(as) && as.every(isBoolean) /** * Type guard for the `Array` type */ export const isNumberArray = (as: unknown): as is Array => isArray(as) && as.every(isNumber) /** * Type guard for the `Array` type */ export const isStringArray = (as: unknown): as is Array => isArray(as) && as.every(isString) /** * Typeguard for making sure a key is in an object when the object has no index signature */ export function hasKey( obj: O, key: K, ): obj is O & { [k in K]: unknown } { return key in obj } /** * Type guard to check for an instance of a DOM Element */ export const isDOMElement = (a: unknown): a is Element => a instanceof Element