ts-utils — TypeScript Utility Functions
A lightweight collection of TypeScript utility functions for common operations. Zero dependencies, tree-shakeable, fully typed. Includes array helpers, string manipulation, object utilities, and type guards.
Published March 21, 2026
Installation
npm install @shopubu/ts-utils
# or
pnpm add @shopubu/ts-utils
# or
yarn add @shopubu/ts-utils
Quick Start
import { groupBy, pick, slugify, isNonNullable } from '@shopubu/ts-utils'
// Group array items by key
const grouped = groupBy(users, 'role')
// { admin: [...], user: [...] }
// Pick specific keys from object
const subset = pick(user, ['name', 'email'])
// { name: '...', email: '...' }
// Generate URL-safe slugs
const slug = slugify('Hello World!')
// 'hello-world'
// Type-safe null filtering
const values = [1, null, 2, undefined, 3].filter(isNonNullable)
// Type: number[] (not (number | null | undefined)[])
API Reference
Array Utilities
groupBy<T>(array: T[], key: keyof T): Record<string, T[]>
Groups array elements by a key property. Returns a new object without mutating the input.
const products = [
{ name: 'Shirt', category: 'clothing' },
{ name: 'Pants', category: 'clothing' },
{ name: 'Phone', category: 'electronics' },
]
const grouped = groupBy(products, 'category')
// { clothing: [Shirt, Pants], electronics: [Phone] }
chunk<T>(array: T[], size: number): T[][]
Splits an array into chunks of the specified size. The last chunk may be smaller.
chunk([1, 2, 3, 4, 5], 2)
// [[1, 2], [3, 4], [5]]
unique<T>(array: T[], key?: keyof T): T[]
Returns unique elements. When a key is provided, uniqueness is determined by that property.
unique([1, 2, 2, 3, 3, 3])
// [1, 2, 3]
unique(users, 'email')
// Unique users by email
compact<T>(array: (T | null | undefined | false | 0 | '')[]): T[]
Removes all falsy values from an array with proper type narrowing.
compact([0, 1, false, 2, '', 3, null, undefined])
// [1, 2, 3] — Type: number[]
Object Utilities
pick<T, K extends keyof T>(obj: T, keys: K[]): Pick<T, K>
Creates a new object with only the specified keys. Never mutates the original.
const user = { name: 'Fatih', email: '[email protected]', password: 'secret' }
const safe = pick(user, ['name', 'email'])
// { name: 'Fatih', email: '[email protected]' }
omit<T, K extends keyof T>(obj: T, keys: K[]): Omit<T, K>
Creates a new object without the specified keys.
const user = { name: 'Fatih', email: '[email protected]', password: 'secret' }
const safe = omit(user, ['password'])
// { name: 'Fatih', email: '[email protected]' }
deepMerge<T>(target: T, source: Partial<T>): T
Deep merges two objects. Returns a new object — both inputs remain unchanged.
const defaults = { theme: { color: 'blue', size: 'md' }, debug: false }
const overrides = { theme: { color: 'red' } }
const config = deepMerge(defaults, overrides)
// { theme: { color: 'red', size: 'md' }, debug: false }
String Utilities
slugify(str: string): string
Converts a string to a URL-safe slug. Handles unicode, diacritics, and special characters.
slugify('Hello World!') // 'hello-world'
slugify('Türkçe Karakterler') // 'turkce-karakterler'
slugify(' Multiple Spaces ') // 'multiple-spaces'
truncate(str: string, length: number, suffix?: string): string
Truncates a string at the specified length, breaking at word boundaries. Default suffix is ....
truncate('This is a long sentence that needs trimming', 20)
// 'This is a long...'
truncate('Custom suffix example', 15, ' [more]')
// 'Custom suffix [more]'
Type Guards
isNonNullable<T>(value: T): value is NonNullable<T>
Type-safe null/undefined check that narrows the type correctly. Essential for .filter() chains.
const items: (string | null | undefined)[] = ['a', null, 'b', undefined]
const filtered = items.filter(isNonNullable)
// Type: string[] (correctly narrowed)
isString(value: unknown): value is string
Runtime string type check with type narrowing.
const value: unknown = getInput()
if (isString(value)) {
// value is now typed as string
value.toUpperCase()
}
isNumber(value: unknown): value is number
Runtime number type check. Excludes NaN.
isNumber(42) // true
isNumber('42') // false
isNumber(NaN) // false
isNumber(Infinity) // true
Design Principles
- Zero dependencies — No external packages. The entire library adds minimal bundle impact.
- Tree-shakeable — Import only what you use. Bundlers eliminate unused exports.
- Fully typed — Generic types preserved through all transformations. No
anytypes. - Immutable — Every function returns a new value. Inputs are never mutated.
- Tested — Each function has comprehensive unit tests covering edge cases.
Bundle Size
| Export | Size (gzipped) |
|---|---|
groupBy | ~120B |
pick / omit | ~90B each |
slugify | ~180B |
isNonNullable | ~30B |
| Full package | ~1.2KB |
Note: This is a showcase entry demonstrating the package documentation template. The package structure and API design reflect real-world TypeScript utility patterns.