Utilities (es-toolkit)

Overview

Check es-toolkit before writing any utility function. It likely already exists with better edge-case handling, tree-shaking, and type safety. These rules cover which es-toolkit functions to reach for and when to write your own instead.

Rules

Use Type Guards for Runtime Checks

Functions for runtime type checking. Prefer these over manual typeof chains.

FunctionDescriptionExample
isNilCheck null or undefinedisNil(value)
isNullCheck null onlyisNull(value)
isUndefinedCheck undefined onlyisUndefined(value)
isStringCheck stringisString(value)
isNumberCheck number (not NaN)isNumber(value)
isBooleanCheck booleanisBoolean(value)
isPlainObjectCheck plain objectisPlainObject(value)
isArrayCheck arrayisArray(value)
isFunctionCheck functionisFunction(value)

Correct

import { isNil, isPlainObject, isString } from 'es-toolkit'

// Filter out nil values
if (!isNil(value)) {
  // value is not null or undefined
}

// Validate payload structure
if (isPlainObject(payload) && isString(payload.action)) {
  // payload is a plain object with string action
}

Use Object Utilities for Immutable Transforms

Functions for picking, omitting, and transforming object properties without mutation.

FunctionDescriptionExample
pickSelect propertiespick(obj, ['a', 'b'])
omitExclude propertiesomit(obj, ['secret'])
omitByExclude by predicateomitBy(obj, isNil)
pickBySelect by predicatepickBy(obj, isString)
mapValuesTransform valuesmapValues(obj, fn)
mapKeysTransform keysmapKeys(obj, fn)
mergeDeep merge objectsmerge(target, source)
cloneShallow cloneclone(obj)
cloneDeepDeep clonecloneDeep(obj)

Correct

import { pick, omit, omitBy, isNil } from 'es-toolkit'

// Select specific fields for display
const summary = pick(config, ['name', 'root', 'scripts'])

// Remove internal fields before serializing
const safeConfig = omit(config, ['_resolved', '_path'])

// Remove nil values before writing config
const cleanConfig = omitBy(rawConfig, isNil)

Use Collection Utilities for Arrays

Functions for grouping, deduplicating, and batching arrays.

FunctionDescriptionExample
groupByGroup by key/functiongroupBy(scripts, 'workspace')
keyByCreate lookup by keykeyBy(scripts, 'name')
chunkSplit into chunkschunk(items, 10)
uniqRemove duplicatesuniq(array)
uniqByRemove duplicates by keyuniqBy(scripts, 'name')
differenceItems in first not seconddifference(a, b)
intersectionItems in bothintersection(a, b)
compactRemove falsy valuescompact(array)
flattenFlatten one levelflatten(nested)
flattenDeepFlatten all levelsflattenDeep(nested)

Correct

import { groupBy, keyBy, chunk, uniqBy } from 'es-toolkit'

// Group scripts by workspace
const scriptsByWorkspace = groupBy(scripts, 'workspace')
// { root: [...], packages/core: [...] }

// Create name lookup
const scriptsByName = keyBy(scripts, 'name')
// { build: script1, lint: script2 }

// Process workspaces in batches
const batches = chunk(workspaces, 10)
await batches.reduce((chain, batch) => chain.then(() => processBatch(batch)), Promise.resolve())

// Remove duplicate script names
const uniqueScripts = uniqBy(scripts, 'name')

Use Function Utilities for Scheduling and Caching

Functions for controlling execution timing and caching results.

FunctionDescriptionExample
debounceDelay until pausedebounce(fn, 300)
throttleLimit call frequencythrottle(fn, 1000)
memoizeCache resultsmemoize(fn)
onceCall only onceonce(fn)
noopNo-op functionnoop
identityReturn inputidentity

Correct

import { debounce, throttle, memoize } from 'es-toolkit'

// Debounce file watcher callback to avoid redundant rebuilds
const onFileChange = debounce((path: string) => {
  rebuildWorkspace(path)
}, 300)

// Throttle log output to at most once per second
const logProgress = throttle((message: string) => {
  process.stdout.write(`\r${message}`)
}, 1000)

// Cache expensive config resolution
const resolveConfig = memoize((root: string) => {
  return loadAndMergeConfig(root)
})

Use String Utilities for Case Conversion

Functions for converting between naming conventions.

FunctionDescriptionExample
camelCaseConvert to camelCasecamelCase('foo-bar')
kebabCaseConvert to kebab-casekebabCase('fooBar')
snakeCaseConvert to snake_casesnakeCase('fooBar')
capitalizeUppercase first lettercapitalize('hello')
trimRemove whitespacetrim(' hello ')

Correct

import { camelCase, kebabCase } from 'es-toolkit'

// Convert config keys from snake_case
const configKey = 'script_timeout'
const jsKey = camelCase(configKey) // 'scriptTimeout'

// Convert to kebab-case for file names
const moduleName = 'ConfigLoader'
const fileName = kebabCase(moduleName) // 'config-loader'

Avoid es-toolkit for Trivial Operations

Do not import es-toolkit for checks that are clearer as one-liners. Reserve it for operations that are genuinely hard to get right.

Correct

// Simple null check - inline is clearer
if (x != null) {
  // ...
}

// Complex grouping - use es-toolkit
const grouped = groupBy(scripts, 'workspace')
const batches = chunk(workspaces, 100)

Incorrect

import { isNil } from 'es-toolkit'

// Overkill for a simple null check
if (!isNil(x)) {
  // ...
}

Resources

References