Last active
October 26, 2024 19:44
-
-
Save MarwanShehata/3ae565ca4b806ece1e911545806f082c to your computer and use it in GitHub Desktop.
This is not a library, this is just a list of functions I decided to put in a central location so I can copy them to future projects as needed. These functions are in typescript an do both compile-time (with generics) AND runtime validation. Feel fr
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// **** Types **** // | |
export type TFunc = (...args: any[]) => any; | |
export type TEmail = `${string}@${string}`; | |
export type TColor = `#${string}`; | |
// **** Variables **** // | |
const EMAIL_RGX = /^(?!\.)(?!.*\.\.)([A-Z0-9_'+\-.]*)[A-Z0-9_+-]@([A-Z0-9][A-Z0-9-]*\.)+[A-Z]{2,}$/i, | |
COLOR_RGX = new RegExp(/^#([A-Fa-f0-9]{6}|[A-Fa-f0-9]{3})$/), | |
ALPHA_NUMERIC = new RegExp('^[a-zA-Z0-9]*$'); | |
// **** Functions **** // | |
// Nullables | |
export const isUndef = ((arg: unknown): arg is undefined => arg === undefined); | |
export const isNull = ((arg: unknown): arg is null => arg === null); | |
export const isNoU = orNul(isUndef); | |
// Boolean | |
export const isBool = checkType<boolean>('boolean'); | |
export const isOptBool = orOpt(isBool); | |
export const isNulBool = orNul(isBool); | |
export const isOptNulBool = orNul(isOptBool); | |
export const isBoolArr = isArr(isBool); | |
export const isOptBoolArr = orOpt(isBoolArr); | |
export const isNulBoolArr = orNul(isBoolArr); | |
export const isOptNulBoolArr = orNul(isOptBoolArr); | |
// Number | |
export const isNum = checkType<number>('number'); | |
export const isOptNum = orOpt(isNum); | |
export const isNulNum = orNul(isNum); | |
export const isOptNulNum = orNul(isOptNum); | |
export const isNumArr = isArr(isNum); | |
export const isOptNumArr = orOpt(isNumArr); | |
export const isNulNumArr = orNul(isNumArr); | |
export const isOptNulNumArr = orNul(isOptNumArr); | |
// String | |
export const isStr = checkType<string>('string'); | |
export const isOptStr = orOpt(isStr); | |
export const isNulStr = orNul(isStr); | |
export const isOptNulStr = orNul(isOptStr); | |
export const isStrArr = isArr(isStr); | |
export const isOptStrArr = orOpt(isStrArr); | |
export const isNulStrArr = orNul(isStrArr); | |
export const isOptNulStrArr = orNul(isOptStrArr); | |
// Date | |
export const isDate = (arg: unknown): arg is Date => arg instanceof Date; | |
export const isOptDate = orOpt(isDate); | |
export const isNulDate = orNul(isDate); | |
export const isOptNulDate = orNul(isOptDate); | |
export const isDateArr = isArr(isDate); | |
export const isOptDateArr = orOpt(isDateArr); | |
export const isNulDateArr = orNul(isDateArr); | |
export const isOptNulDateArr = orNul(isOptDateArr); | |
// Object | |
export const isObj = checkType<object>('object'); | |
export const isOptObj = orOpt(isObj); | |
export const isNulObj = orNul(isObj); | |
export const isOptNulObj = orNul(isOptObj); | |
export const isObjArr = isArr(isObj); | |
export const isOptObjArr = orOpt(isObjArr); | |
export const isNulObjArr = orNul(isObjArr); | |
export const isOptNulObjArr = orNul(isOptObjArr); | |
// Function | |
export const isFn = checkType<TFunc>('function'); | |
export const isOptFn = orOpt(isFn); | |
export const isNulFn = orNul(isFn); | |
export const isOptNulFn = orNul(isOptFn); | |
export const isFnArr = isArr(isFn); | |
export const isOptFnArr = orOpt(isFnArr); | |
export const isNulFnArr = orNul(isFnArr); | |
export const isOptNulFnArr = orNul(isOptFnArr); | |
// Color | |
export const isColor = isRgx<TColor>(COLOR_RGX); | |
export const isOptColor = orOpt(isColor); | |
export const isNulColor = orNul(isColor); | |
export const isOptNulColor = orNul(isOptColor); | |
export const isEmail = isRgx<TEmail>(EMAIL_RGX); | |
export const isOptEmail = orOpt(isEmail); | |
export const isNulEmail = orNul(isEmail); | |
export const isOptNulEmail = orNul(isOptEmail); | |
// Alpha-Numeric String | |
export const isAlphaNumStr = isRgx<string>(ALPHA_NUMERIC); | |
export const isOptAlphaNumStr = orOpt(isAlphaNumStr); | |
export const isNulAlphaNumStr = orNul(isAlphaNumStr); | |
export const isOptNulAlphaNumStr = orNul(isOptAlphaNumStr); | |
// **** Array Content **** // | |
/** | |
* Is an item in an array. | |
*/ | |
export function isInArr<T extends readonly unknown[]>( | |
arr: T, | |
): (arg: unknown) => arg is T[number] { | |
return (arg: unknown): arg is T[number] => { | |
for (const item of arr) { | |
if (arg === item) { | |
return true; | |
} | |
} | |
return false; | |
}; | |
} | |
/** | |
* Is an item in an array or undefined. | |
*/ | |
export function isOptOrInArr<T extends readonly unknown[]>( | |
arr: T, | |
): (arg: unknown) => arg is T[number] | undefined { | |
const fn = isInArr<T>(arr); | |
return (arg: unknown): arg is T[number] | undefined => { | |
if (arg === undefined) { | |
return true; | |
} | |
return fn(arg); | |
}; | |
} | |
// **** Enums **** // | |
/** | |
* Check is value satisfies enum. | |
*/ | |
export function isEnumVal<T>(arg: T): ((arg: unknown) => arg is T) { | |
const vals = _getEnumVals(arg); | |
return (arg: unknown): arg is T => { | |
return vals.some(val => arg === val); | |
} | |
} | |
/** | |
* Get the values of an enum object. | |
*/ | |
function _getEnumVals(arg: unknown) { | |
if (isNonArrObj(arg)) { | |
const keys = _getEnumKeys(arg); | |
return keys.map(key => arg[key]); | |
} | |
throw Error('"getEnumVals" be an non-array object'); | |
} | |
/** | |
* Get the keys of an enum object. | |
*/ | |
export function _getEnumKeys(arg: unknown): string[] { | |
if (isNonArrObj(arg)) { | |
return Object.keys(arg).reduce((arr: any[], key) => { | |
if (!arr.includes(key)) { | |
arr.push(arg[key]); | |
} | |
return arr; | |
}, []); | |
} | |
throw Error('"getEnumKeys" be an non-array object'); | |
} | |
// **** Misc **** // | |
/** | |
* Extract null/undefined from a validator function. | |
*/ | |
export function nonNullable<T>(cb: ((arg: unknown) => arg is T)) { | |
return (arg: unknown): arg is NonNullable<T> => { | |
if (isNoU(arg)) { | |
return false; | |
} else { | |
return cb(arg); | |
} | |
}; | |
} | |
/** | |
* Check if non object array. | |
*/ | |
export function isNonArrObj( | |
arg: unknown, | |
): arg is Record<string, unknown> { | |
return typeof arg === 'object' && !Array.isArray(arg); | |
} | |
/** | |
* Do a validator callback function for each object key/value pair. | |
*/ | |
export function checkObjEntries( | |
val: unknown, | |
cb: (key: string, val: unknown) => boolean, | |
): val is NonNullable<object> { | |
if (isObj(val)) { | |
for (const entry of Object.entries(val)) { | |
if (!cb(entry[0], entry[1])) { | |
return false; | |
} | |
} | |
} | |
return true; | |
} | |
// **** Wrapper Functions **** // | |
/** | |
* Allow param to be undefined | |
*/ | |
function orOpt<T>(cb: ((arg: unknown) => arg is T)) { | |
return (arg: unknown): arg is (T | undefined) => { | |
if (isUndef(arg)) { | |
return true; | |
} else { | |
return cb(arg); | |
} | |
}; | |
} | |
/** | |
* Allow param to be undefined | |
*/ | |
function orNul<T>(cb: ((arg: unknown) => arg is T)) { | |
return (arg: unknown): arg is (T | null) => { | |
if (arg === null) { | |
return true; | |
} else { | |
return cb(arg); | |
} | |
}; | |
} | |
/** | |
* Check array counterpart for validator item. | |
*/ | |
function isArr<T>(cb: ((arg: unknown) => arg is T)) { | |
return (arg: unknown): arg is T[] => { | |
return Array.isArray(arg) && !arg.some(item => !cb(item)); | |
}; | |
} | |
/** | |
* See if a string satisfies the regex. NOTE: this lets an empty string be a | |
* valid value. | |
*/ | |
function isRgx<T>(rgx: RegExp) { | |
return (arg: unknown): arg is T => { | |
return (isStr(arg) && (arg === '' || rgx.test(arg))); | |
} | |
} | |
/** | |
* Wrapper to check basic type. | |
*/ | |
function checkType<T>(type: string) { | |
return (arg: unknown): arg is T => { | |
return typeof arg === type && (type === 'object' ? (arg !== null) : true); | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment