Last active
October 13, 2023 11:47
-
-
Save tlux/043ab5cc8ad86a29dfcce5dce11ba6cb to your computer and use it in GitHub Desktop.
Some Typescript utility functions for the real world
This file contains 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
/** | |
* Creates a deep clone of an object. | |
* | |
* Internally uses `structuredClone` when available and | |
* `JSON.parse(JSON.stringify(obj))` as fallback. | |
* | |
* @param obj - The object to be cloned. | |
* @return The deep clone of the object. | |
*/ | |
export default function cloneDeep<T>(value: T): T { | |
if (typeof structuredClone === "undefined") { | |
return JSON.parse(JSON.stringify(value)); | |
} | |
return structuredClone(value); | |
} |
This file contains 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
/** | |
* Filters out any undefined or null values from the given list. | |
* Returns a new array. | |
* | |
* @param list - The list to be compacted. | |
* @return The compacted list. | |
*/ | |
export default function compact<T>(list: T[]) { | |
return list.filter((item) => item != null) as NonNullable<T>[]; | |
} |
This file contains 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
/** | |
* A function that constrains the values object to have only the keys that are | |
* present in the defaults object. It returns a new object that contains only | |
* the keys from values that are also present in defaults, with their | |
* corresponding values. | |
* | |
* @param defaults - An object representing the default values. | |
* @param values - An object representing the values to be combined with the | |
* default values. | |
* @return An object that contains the combined properties of `defaults` and | |
* `values`. | |
*/ | |
export default function constrain<T extends Record<string, unknown>>( | |
defaults: T, | |
values: Partial<T>, | |
): T { | |
return Object.keys(defaults).reduce((acc, key: keyof T) => { | |
const value = values[key]; | |
acc[key] = typeof value !== 'undefined' ? value : defaults[key]; | |
return acc; | |
}, {} as T); | |
} |
This file contains 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
type Predicate<T> = (value: T, index: number, array: T[]) => boolean; | |
/** | |
* Finds the indices of elements in an array that satisfy a given condition. | |
* | |
* @param array The array to search through. | |
* @param predicate The condition that an element must satisfy. | |
* @return An array containing the indices of elements that satisfy the condition. | |
*/ | |
export default function findIndices<T>(array: T[], predicate: Predicate<T>) { | |
const indices: number[] = []; | |
array.forEach((value, index) => { | |
if (!predicate(array[i], i, array)) return; | |
indices.push(i); | |
}); | |
return indices; | |
} |
This file contains 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
/** | |
* Checks if the given object has a specific property. | |
* | |
* @param obj - The object to check. | |
* @param key - The property key to check for. | |
* @return Returns true if the object has the property, false otherwise. | |
*/ | |
export default function has< | |
T extends Record<keyof any, unknown>, | |
K extends keyof T, | |
>(obj: T, key: K): obj is T & { [P in K]-?: T[P] } { | |
return key in obj; | |
} |
This file contains 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
/** | |
* Checks if a value is a boolean. | |
* | |
* @param value - The value to check. | |
* @return Returns true if the value is a boolean, otherwise returns false. | |
*/ | |
export default function isBoolean(value: unknown): value is boolean { | |
return typeof value === 'boolean'; | |
} |
This file contains 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
/** | |
* Checks if a given value is an object with string keys and any values. | |
* | |
* @param value - The value to check. | |
* @return Returns true if the value is an object with string keys and any | |
* values, otherwise false. | |
*/ | |
export default function isFieldValues( | |
value: unknown, | |
): value is Record<string, any> { | |
return isRecord(value) && Object.getOwnPropertySymbols(value).length === 0; | |
} |
This file contains 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
/** | |
* Checks if a value is a float. | |
* | |
* @param value - The value to check. | |
* @return Returns true if the value is a float, otherwise returns false. | |
*/ | |
export default function isFloat(value: unknown): value is number { | |
return typeof value === 'number' && !Number.isInteger(value); | |
} |
This file contains 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
/** | |
* Checks if a value is a function. | |
* | |
* @param value The value to check. | |
* @return Returns true if the value is a function, otherwise false. | |
*/ | |
export default function isFunction(value: unknown): value is (...args: any[]) => any { | |
return typeof value === 'function'; | |
} |
This file contains 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
/** | |
* Checks if a value is an integer. | |
* | |
* @param value - The value to check. | |
* @return Returns true if the value is an integer, otherwise returns false. | |
*/ | |
export default function isInteger(value: unknown): value is number { | |
return typeof value === 'number' && Number.isInteger(value); | |
} |
This file contains 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
/** | |
* Determines if the given object is null or undefined. | |
* | |
* @param obj - The object to check. | |
* @returns True if the object is null or undefined, false otherwise. | |
*/ | |
export default function isNil<T>(value: T): value is Extract<T, null | undefined> { | |
return value == null; | |
} |
This file contains 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
/** | |
* Checks if the given object is not null or undefined. | |
* | |
* @param obj - The object to be checked. | |
* @return Returns true if the object is not null or undefined, false otherwise. | |
*/ | |
export default function isNonNil<T>(value: T): value is NonNullable<T> { | |
return value != null; | |
} |
This file contains 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
/** | |
* Checks if a value is a number. | |
* | |
* @param value - The value to check. | |
* @return Returns true if the value is a number, otherwise returns false. | |
*/ | |
export default function isNumber(value: unknown): value is number { | |
return typeof value === 'number'; | |
} |
This file contains 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
/** | |
* Checks if the given value is a `Record`. | |
* | |
* A `Record` is a plain object with string, number or symbol key. | |
* | |
* @param value - The value to be checked. | |
* @return Returns true if the value is a record, false otherwise. | |
*/ | |
export default function isRecord( | |
value: unknown, | |
): value is Record<keyof any, any> { | |
if (!value || typeof value !== 'object' || Array.isArray(value)) { | |
return false; | |
} | |
// credits to the author of lodash's isPlainObject function for | |
// the following part of the implementation | |
if (Object.getPrototypeOf(value) === null) { | |
return true; | |
} | |
let prototype = value; | |
while (Object.getPrototypeOf(prototype) !== null) { | |
prototype = Object.getPrototypeOf(prototype); | |
} | |
return Object.getPrototypeOf(value) === prototype; | |
} |
This file contains 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
/** | |
* Checks if a value is a string. | |
* | |
* @param value - The value to check. | |
* @return Returns true if the value is a string, otherwise returns false. | |
*/ | |
export default function isString(value: unknown): value is string { | |
return typeof value === 'string'; | |
} |
This file contains 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
type Comparator<T> = (item: T, other: T) => number; | |
/** | |
* Calculates the maximum value in an array by applying a mapper function to | |
* each element and comparing the results using a custom comparator function. | |
* | |
* @param array The array of elements to search. | |
* @param mapper The function to apply to each element in the array. | |
* @param comparator The function used to compare the results of the mapper | |
* function. | |
* @returns The maximum element in the array, or undefined if the array is | |
* empty. | |
*/ | |
export default function maxByWith<T, U>( | |
array: T[], | |
mapper: (item: T) => U, | |
comparator: Comparator<U>, | |
): T | undefined { | |
if (!array.length) return undefined; | |
return array.reduce((prev, curr) => { | |
return comparator(mapper(prev), mapper(curr)) > 0 ? prev : curr; | |
}, array[0]); | |
} |
This file contains 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
type Comparator<T> = (item: T, other: T) => number; | |
/** | |
* Calculates the maximum value in an array using a custom comparator function. | |
* | |
* @param array The array of elements to search. | |
* @param comparator The function used to compare the results of the mapper | |
* function. | |
* @returns The maximum element in the array, or undefined if the array is | |
* empty. | |
*/ | |
export default function maxWith<T>( | |
array: T[], | |
comparator: Comparator<T>, | |
): T | undefined { | |
if (!array.length) return undefined; | |
return array.reduce((prev, curr) => { | |
return comparator(prev, curr) > 0 ? prev : curr; | |
}, array[0]); | |
} |
This file contains 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
/** | |
* Moves an item within an array from one index to another. Mutates the passed | |
* array. | |
* | |
* @param array The array to modify. | |
* @param from The index of the item to move. | |
* @param to The index to move the item to. | |
* @return The modified array. | |
*/ | |
export default function move<T>(array: T[], from: number, to: number) { | |
const toIndex = to < 0 ? array.length - to : to; | |
const item = array.splice(from, 1)[0]; | |
array.splice(toIndex, 0, item); | |
return array; | |
} |
This file contains 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
import move from './move'; | |
/** | |
* Moves an element within an array by a specified offset. Mutates the passed | |
* array. | |
* | |
* @param array The array to modify. | |
* @param index The index of the element to move. | |
* @param offset The offset by which to move the element. | |
* @return The modified array with the element moved. | |
*/ | |
export default function moveBy<T>(array: T[], index: number, offset: number) { | |
if (offset === 0) return array; | |
const toIndex = Math.max(index + offset, 0); | |
return move(array, index, toIndex); | |
} |
This file contains 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
/** | |
* Omits the specified keys from an object and returns a new object without | |
* those keys. | |
* | |
* @param obj - The object to omit keys from. | |
* @param keys - The keys to omit from the object. | |
* @returns A new object with only the picked keys. | |
*/ | |
export default function omit< | |
T extends Record<string, unknown>, | |
K extends keyof T, | |
>(obj: T, keys: K[]): Omit<T, K> { | |
return keys.reduce( | |
(acc, key) => { | |
delete acc[key]; | |
return acc; | |
}, | |
{ ...obj }, | |
); | |
} |
This file contains 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
/** | |
* Picks the specified keys from an object and returns a new object with only | |
* those keys. | |
* | |
* @param obj - The object to pick keys from. | |
* @param keys - The keys to pick from the object. | |
* @returns A new object with only the picked keys. | |
*/ | |
export default function pick< | |
T extends Record<string, unknown>, | |
K extends keyof T, | |
>(obj: T, keys: K[]): Pick<T, K> { | |
return keys.reduce( | |
(acc, key) => { | |
acc[key] = obj[key]; | |
return acc; | |
}, | |
{} as Pick<T, K>, | |
); | |
} |
This file contains 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
type Equatable<T> = (value: T, other: T) => boolean; | |
const DEFAULT_EQUATABLE: Equatable<unknown> = (a, b) => a === b; | |
/** | |
* Remove the first occurrence of a value from an array. Mutates the passed | |
* array. | |
* | |
* @param list - The array from which the value will be removed. | |
* @param value - The value to be removed. | |
* @param isEqual - The equality function used to compare values. Defaults to | |
* the default equality function. | |
* @return The modified array with the first occurrence of the value removed. | |
*/ | |
export default function pull<T>( | |
list: T[], | |
value: T, | |
isEqual: Equatable<T> = DEFAULT_EQUATABLE, | |
) { | |
const index = list.findIndex((item) => isEqual(item, value)); | |
if (index < 0) return list; | |
list.splice(index, 1); | |
return list; | |
} |
This file contains 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
type Predicate<T> = (value: T, index: number, list: T[]) => boolean; | |
/** | |
* Filters elements from the given list that do not satisfy the provided | |
* predicate function. | |
* | |
* @param list - The list to filter. | |
* @param predicate - The predicate function used to determine if an element | |
* should be filtered. | |
* @return The filtered list. | |
*/ | |
export default function reject<T>(list: T[], predicate: Predicate<T>) { | |
return list.filter((value, index, array) => !predicate(value, index, array)); | |
} |
This file contains 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
type Equatable<T> = (value: T, other: T) => boolean; | |
const DEFAULT_EQUATABLE: Equatable<unknown> = (a, b) => a === b; | |
/** | |
* Removes all occurrences of a specified value from an array. The returned | |
* array is a shallow copy of the original array. | |
* | |
* @param list - The array from which to remove the value. | |
* @param value - The value to be removed from the array. | |
* @param isEqual - (optional) A function that determines whether two values are | |
* equal. If not provided, a default equality function will be used. | |
* @return A new array with all occurrences of the specified value removed. | |
*/ | |
export default function remove<T>( | |
list: T[], | |
value: T, | |
isEqual: Equatable<T> = DEFAULT_EQUATABLE, | |
) { | |
return list.filter((item) => !isEqual(item, value)); | |
} |
This file contains 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
type Comparator<T> = (item: T, other: T) => number; | |
type SortOrder = 'asc' | 'desc'; | |
const DEFAULT_ORDER: SortOrder = 'asc'; | |
/** | |
* Sorts an array. Mutates the passed array. | |
* | |
* @param list - The array to be sorted. | |
* @param comparator - (Optional) A function that defines the sort order. | |
* @return The sorted array. | |
*/ | |
export function sort<T>( | |
list: T[], | |
comparator?: Comparator<T>, | |
order: SortOrder = DEFAULT_ORDER, | |
) { | |
const c = order === 'asc' ? 1 : -1; | |
return list.sort((a, b) => comparator(a, b) * c); | |
} | |
/** | |
* Sorts an array using a custom sorting function. Mutates the passed array. | |
* | |
* @param list - The array to be sorted. | |
* @param with - The sorting function. | |
* @param order - The order of sorting. | |
* @returns - The sorted array. | |
*/ | |
export function sortWith<T, U>( | |
list: T[], | |
fn: (item: T) => U, | |
order = DEFAULT_ORDER, | |
) { | |
return sort(list, (a, b) => (fn(a) > fn(b) ? f : -f), order); | |
} | |
/** | |
* Sorts an array of objects by a specified key. Mutates the passed array. | |
* | |
* @param list - The array of objects to be sorted. | |
* @param key - The key to sort the objects by. | |
* @param order - The order of sorting. | |
* @return The sorted array of objects. | |
*/ | |
export function sortBy<T extends Record<string, unknown>>( | |
list: T[], | |
key: keyof T, | |
order = DEFAULT_ORDER, | |
) { | |
return sortWith(list, (item) => item[key], order); | |
} |
This file contains 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
function calcIndex(index: number, maxIndex: number) { | |
let expandedIndex = index; | |
if (index < 0) { | |
expandedIndex = maxIndex + 1 - index; | |
} | |
return Math.max(0, Math.min(expandedIndex, maxIndex)); | |
} | |
/** | |
* Swaps the elements in an array at the specified old and new indices. Mutates | |
* the passed array. | |
* | |
* @param array The array to perform the swap operation on. | |
* @param from The index of the element to be swapped. | |
* @param to The index where the element will be moved to. | |
* @returns The modified array with the elements swapped. | |
*/ | |
export default function swap<T>(array: T[], from: number, to: number): T[] { | |
const maxIndex = array.length - 1; | |
const fromIndex = calcIndex(from, maxIndex); | |
const toIndex = calcIndex(to, maxIndex); | |
if (fromIndex === toIndex) return array; | |
const sourceItem = array[fromIndex]; | |
const targetItem = array[toIndex]; | |
array[toIndex] = sourceItem; | |
array[fromIndex] = targetItem; | |
return array; | |
} |
This file contains 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
import isFunction from './isFunction'; | |
type Predicate<T> = (value: T, index: number, array: T[]) => boolean; | |
/** | |
* Updates an array by applying an updater function to each element that satisfies the given predicate. | |
* | |
* @param array The array to be updated. | |
* @param where The predicate function used to find the element to be updated. | |
* @param set The function used to update the element. | |
* @returns The updated array. | |
*/ | |
export default function update<T>( | |
array: T[], | |
where: Predicate<T>, | |
set: T | ((value: T, index: number, array: T[]) => T), | |
) { | |
array.forEach((value, index) => { | |
if (!where(value, index, array)) return; | |
array[index] = isFunction(set) ? set(value, index, array) : set; | |
}); | |
return array; | |
} |
This file contains 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
/** | |
* Wraps the given value in an array if it is not already an array. | |
* | |
* @param value - The value to be wrapped. | |
* @returns The wrapped value. | |
*/ | |
export default function wrapArray<T>(value: T | T[] | null | undefined): T[] { | |
if (value == null) return []; | |
if (Array.isArray(value)) return value; | |
return [value]; | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment