utils.ts•3.75 kB
import { deepmerge } from 'deepmerge-ts'
export function isString(str: unknown): str is string {
return str != null && typeof str === 'string'
}
export function isNil<T>(value: T | null | undefined): value is null | undefined {
return value === null || value === undefined
}
// eslint-disable-next-line @typescript-eslint/no-explicit-any
export function setAtPath<T, K extends keyof any>(obj: T, path: K | K[], value: any): void {
const pathArray = Array.isArray(path) ? path : (path as string).match(/([^[.\]])+/g) as unknown as K[]
// eslint-disable-next-line @typescript-eslint/no-explicit-any
pathArray.reduce((acc: any, key: K, i: number) => {
if (acc[key] === undefined) acc[key] = {}
if (i === pathArray.length - 1) acc[key] = value
return acc[key]
}, obj)
}
export function insertAt<T>(array: T[], index: number, item: T): T[] {
return [...array.slice(0, index), item, ...array.slice(index)]
}
export function debounce<T>(func: (...args: T[]) => void, wait: number): (...args: T[]) => void {
let timeout: NodeJS.Timeout
return function (...args: T[]) {
const later = () => {
clearTimeout(timeout)
func(...args)
}
clearTimeout(timeout)
timeout = setTimeout(later, wait)
}
}
type DeepPartial<T> = {
[P in keyof T]?: T[P] extends Record<string, unknown> ? DeepPartial<T[P]> : T[P];
}
/**
* This function also merges arrays, x = [1, 2], y = [3, 4], z = deepMergeAndCast(x, y) -> [1, 2, 3, 4]
**/
export function deepMergeAndCast<T>(target: DeepPartial<T>, source: DeepPartial<T>): T {
return deepmerge(target as Partial<T>, source as Partial<T>) as T
}
export function kebabCase(str: string): string {
return str
.replace(/([a-z])([A-Z])/g, '$1-$2') // Handle camelCase by adding hyphen between lowercase and uppercase letters
.replace(/\s+/g, '-') // Replace spaces with hyphens
.replace(/_/g, '-') // Replace underscores with hyphens
.toLowerCase() // Convert to lowercase
.replace(/^-+|-+$/g, '') // Remove leading and trailing hyphens
}
export function isEmpty<T>(value: T | null | undefined): boolean {
if (value == null) {
return true
}
if (typeof value === 'string' || Array.isArray(value)) {
return value.length === 0
}
if (typeof value === 'object') {
return Object.keys(value).length === 0
}
return false
}
export function startCase(str: string): string {
return str
.replace(/([a-z])([A-Z])/g, '$1 $2')
.replace(/[_-]+/g, ' ')
.replace(/\s+/g, ' ')
.replace(/^[a-z]/, match => match.toUpperCase())
.replace(/\b[a-z]/g, match => match.toUpperCase())
}
export function camelCase(str: string): string {
return str
.replace(/([-_][a-z])/g, group => group.toUpperCase()
.replace('-', '')
.replace('_', ''))
}
export function parseToJsonIfPossible(str: unknown): unknown {
try {
return JSON.parse(str as string)
}
catch (e) {
return str
}
}
export function pickBy<T extends Record<string, unknown>>(
object: T,
predicate: (value: T[keyof T], key: keyof T) => boolean,
): Partial<T> {
return Object.keys(object).reduce((result: Partial<T>, key: keyof T) => {
if (predicate(object[key], key)) {
result[key] = object[key]
}
return result
}, {})
}
export function chunk<T>(records: T[], size: number) {
const chunks: T[][] = []
for (let i = 0; i < records.length; i += size) {
chunks.push(records.slice(i, i + size))
}
return chunks
}