npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@tienedev/datype

v0.1.0

Published

Modern TypeScript utility library with pragmatic typing and zero dependencies

Readme

datype

A modern, TypeScript-first utility library with perfect type inference and zero dependencies.

npm version TypeScript Test Coverage License: MIT Bundle Size GitHub Stars

InstallationAPI Reference

Why datype?

datype is built for the modern TypeScript ecosystem. Unlike traditional utility libraries, every function is designed with TypeScript-first principles, providing perfect type inference without manual type annotations.

import { deepMerge, pick, groupBy } from 'datype';

// ✨ Perfect type inference - no manual typing needed
const config = deepMerge(
  { api: { timeout: 5000 }, features: ['auth'] },
  { api: { retries: 3 }, debug: true }
);
// Type: { api: { timeout: number; retries: number }; features: string[]; debug: boolean }

const users = [
  { name: 'Alice', role: 'admin', age: 32 },
  { name: 'Bob', role: 'user', age: 28 }
];

// Type-safe property selection
const publicData = pick(users[0], ['name', 'role']);
// Type: { name: string; role: string }

// Smart grouping with automatic type inference
const byRole = groupBy(users, 'role');
// Type: Record<string, { name: string; role: string; age: number }[]>

Features

  • 🎯 TypeScript-first - Pragmatic typing that prioritizes usability
  • 📦 Tree-shakable - Optimal bundle size by design
  • 🔒 Immutable - All operations return new objects/arrays
  • Modern - Built for ES2020+ environments
  • 🛡️ Zero dependencies - No external dependencies
  • 📊 Lightweight - ~5KB gzipped for the full library

Installation

npm install datype
yarn add datype
pnpm add datype

Quick Start

import { 
  deepMerge, pick, omit, get, set,
  debounce, throttle, isEmpty, isEqual,
  groupBy, mapValues, slugify
} from 'datype';

// 🏗️ Object manipulation
const merged = deepMerge(defaults, userConfig);
const subset = pick(user, ['id', 'name', 'email']);
const cleaned = omit(data, ['password', 'secret']);

// 🔍 Safe property access
const value = get(obj, 'nested.deep.property', 'default');
const updated = set(obj, 'nested.new.path', 'value');

// ⚡ Performance optimization
const debouncedSave = debounce(saveToAPI, 300);
const throttledScroll = throttle(onScroll, 16);

// ✅ Validation and comparison
if (isEmpty(formData.email)) { /* handle empty */ }
if (isEqual(prevState, newState)) { /* skip update */ }

// 📊 Data transformation
const grouped = groupBy(items, 'category');
const transformed = mapValues(obj, value => value.toString());
const slug = slugify('Hello World!'); // 'hello-world'

API Reference

Object Utilities

deepMerge<T, U>(target: T, source: U, options?: DeepMergeOptions): DeepMergeResult<T, U>

Deep merge objects with intelligent type inference and configurable array handling.

const defaults = { api: { timeout: 5000 }, features: ['auth'] };
const config = { api: { retries: 3 }, features: ['logging'], debug: true };

// Merge with array concatenation (default)
const result = deepMerge(defaults, config);
// { api: { timeout: 5000, retries: 3 }, features: ['auth', 'logging'], debug: true }

// Merge with array replacement
const result2 = deepMerge(defaults, config, { arrayMerge: 'replace' });
// { api: { timeout: 5000, retries: 3 }, features: ['logging'], debug: true }

pick<T, K extends keyof T>(obj: T, keys: K[]): Pick<T, K>

Extract specific properties with perfect type safety.

const user = { id: 1, name: 'Alice', email: '[email protected]', password: 'secret' };
const safeUser = pick(user, ['id', 'name', 'email']);
// Type: { id: number; name: string; email: string }

omit<T, K extends keyof T>(obj: T, keys: K[]): Omit<T, K>

Exclude specific properties with type safety.

const user = { id: 1, name: 'Alice', email: '[email protected]', password: 'secret' };
const publicUser = omit(user, ['password']);
// Type: { id: number; name: string; email: string }

get<T>(obj: T, path: string, defaultValue?: any): any

Safe nested property access with dot notation.

const data = { user: { profile: { name: 'Alice' } } };

get(data, 'user.profile.name'); // 'Alice'
get(data, 'user.profile.age', 25); // 25 (default)
get(data, 'nonexistent.path'); // undefined

set<T>(obj: T, path: string, value: any): any

Immutably set nested properties with flexible return typing.

const obj = { a: { b: 1 } };
const updated = set(obj, 'a.c', 2);
// { a: { b: 1, c: 2 } }
// Original obj is unchanged

mapValues<T, U>(obj: Record<string, T>, iteratee: (value: T, key: string) => U): Record<string, U>

Transform all object values.

const scores = { alice: '95', bob: '87', charlie: '92' };
const numeric = mapValues(scores, score => parseInt(score, 10));
// { alice: 95, bob: 87, charlie: 92 }

mapKeys<T>(obj: Record<string, T>, iteratee: (key: string) => string): Record<string, T>

Transform all object keys with built-in transformers.

import { mapKeys, keyTransformers } from 'datype';

const apiData = { 'user_name': 'Alice', 'user_email': '[email protected]' };
const camelCased = mapKeys(apiData, keyTransformers.camelCase);
// { userName: 'Alice', userEmail: '[email protected]' }

// Available transformers: camelCase, kebabCase, snakeCase, pascalCase, constantCase, dotCase

Array Utilities

chunk<T>(array: T[], size: number): T[][]

Split array into chunks of specified size.

const numbers = [1, 2, 3, 4, 5, 6, 7, 8];
const chunked = chunk(numbers, 3);
// [[1, 2, 3], [4, 5, 6], [7, 8]]

flatten(array: any[]): any[] / flattenDeep(array: any[]): any[]

Flatten nested arrays.

const nested = [1, [2, 3], [4, [5, 6]]];

flatten(nested);     // [1, 2, 3, 4, [5, 6]]
flattenDeep(nested); // [1, 2, 3, 4, 5, 6]

uniq<T>(array: T[]): T[] / uniqBy<T>(array: T[], iteratee: (item: T) => any): T[]

Remove duplicates.

const numbers = [1, 2, 2, 3, 3, 3];
uniq(numbers); // [1, 2, 3]

const users = [{ id: 1, name: 'Alice' }, { id: 2, name: 'Bob' }, { id: 1, name: 'Alice' }];
uniqBy(users, user => user.id); // [{ id: 1, name: 'Alice' }, { id: 2, name: 'Bob' }]

compact<T>(array: (T | null | undefined | false | 0 | '')[]): T[]

Remove falsy values.

const mixed = [1, null, 2, undefined, 3, false, 4, '', 5];
compact(mixed); // [1, 2, 3, 4, 5]

Function Utilities

debounce<T extends (...args: any[]) => any>(func: T, delay: number, options?: DebounceOptions): DebouncedFunction<T>

Delay function execution until calls stop.

const searchAPI = debounce((query: string) => {
  console.log('Searching:', query);
}, 300, {
  leading: false,  // Don't execute on leading edge
  trailing: true,  // Execute on trailing edge
  maxWait: 1000   // Force execution after 1s
});

// Rapid calls - only last one executes
searchAPI('java');
searchAPI('javascript');
searchAPI('javascript tutorial'); // This will execute after 300ms

// Control methods
searchAPI.cancel(); // Cancel pending execution
searchAPI.flush();  // Execute immediately

throttle<T extends (...args: any[]) => any>(func: T, delay: number, options?: ThrottleOptions): ThrottledFunction<T>

Limit function execution frequency.

const onScroll = throttle(() => {
  console.log('Scroll position:', window.scrollY);
}, 100, {
  leading: true,  // Execute immediately on first call
  trailing: true  // Execute once more after calls stop
});

window.addEventListener('scroll', onScroll);

once<T extends (...args: any[]) => any>(func: T): T

Ensure function executes only once.

const initialize = once(() => {
  console.log('App initialized');
  // expensive setup code
});

initialize(); // "App initialized"
initialize(); // Nothing happens
initialize(); // Nothing happens

Advanced Function Utilities

compose<T>(...functions: Function[]): (value: T) => any

Compose functions right-to-left.

const addOne = (x: number) => x + 1;
const double = (x: number) => x * 2;
const square = (x: number) => x * x;

const transform = compose(square, double, addOne);
transform(3); // square(double(addOne(3))) = square(8) = 64

pipe<T>(...functions: Function[]): (value: T) => any

Compose functions left-to-right.

const transform = pipe(addOne, double, square);
transform(3); // square(double(addOne(3))) = 64

curry<T extends (...args: any[]) => any>(func: T): T & ((...args: any[]) => any)

Transform function to support flexible partial application with pragmatic typing.

const add = (a: number, b: number, c: number) => a + b + c;
const curriedAdd = curry(add);

// All equivalent:
curriedAdd(1, 2, 3);  // 6
curriedAdd(1)(2, 3);  // 6
curriedAdd(1, 2)(3);  // 6
curriedAdd(1)(2)(3);  // 6

// Partial application
const add5 = curriedAdd(5);
add5(2, 3); // 10

Validation Utilities

isEmpty(value: any): boolean

Check if value is empty.

isEmpty([]);        // true
isEmpty({});        // true
isEmpty('');        // true
isEmpty(null);      // true
isEmpty(undefined); // true
isEmpty(0);         // false
isEmpty(false);     // false
isEmpty([1]);       // false
isEmpty({a: 1});    // false

isEqual(a: any, b: any): boolean

Deep equality comparison with circular reference support.

const obj1 = { a: 1, b: { c: 2 } };
const obj2 = { a: 1, b: { c: 2 } };

obj1 === obj2;       // false
isEqual(obj1, obj2); // true

// Handles complex types
isEqual([1, 2, 3], [1, 2, 3]); // true
isEqual(new Date('2023-01-01'), new Date('2023-01-01')); // true
isEqual(/abc/g, /abc/g); // true

cloneDeep<T>(value: T): T

Create deep clone with circular reference protection.

const original = {
  user: { name: 'Alice' },
  items: [1, 2, { nested: true }]
};

const clone = cloneDeep(original);
clone.user.name = 'Bob';
console.log(original.user.name); // 'Alice' (unchanged)

String Utilities

slugify(text: string, options?: SlugifyOptions): string

Convert text to URL-friendly slug.

slugify('Hello World!'); // 'hello-world'
slugify('Café & Crème'); // 'cafe-and-creme'
slugify('TypeScript is Awesome!', {
  separator: '_',
  maxLength: 20
}); // 'typescript_is_awesome'

String Case Transformers

import { camelCase, kebabCase, snakeCase, pascalCase, constantCase } from 'datype';

camelCase('hello-world');    // 'helloWorld'
kebabCase('HelloWorld');     // 'hello-world'
snakeCase('helloWorld');     // 'hello_world'
pascalCase('hello-world');   // 'HelloWorld'
constantCase('helloWorld');  // 'HELLO_WORLD'

capitalize(text: string): string

Capitalize first letter of each word.

capitalize('hello world');     // 'Hello World'
capitalize('HELLO WORLD');     // 'Hello World'
capitalize('hello-world');     // 'Hello-World'

Text Truncation

import { truncate, truncateWords, truncateMiddle } from 'datype';

truncate('This is a long text', 10); // 'This is a…'
truncateWords('One two three four five', 3); // 'One two three…'
truncateMiddle('very-long-filename.txt', 15); // 'very-lo…ame.txt'

Data Organization

groupBy<T>(array: T[], iteratee: string | ((item: T) => any)): Record<string, T[]>

Group array elements.

const users = [
  { name: 'Alice', role: 'admin', age: 30 },
  { name: 'Bob', role: 'user', age: 25 },
  { name: 'Charlie', role: 'admin', age: 35 }
];

// Group by property
const byRole = groupBy(users, 'role');
// { admin: [Alice, Charlie], user: [Bob] }

// Group by function
const byAgeGroup = groupBy(users, user => user.age >= 30 ? 'senior' : 'junior');
// { senior: [Alice, Charlie], junior: [Bob] }

Type Guards

import { isPlainObject, isArray, isFunction } from 'datype';

isPlainObject({}); // true
isPlainObject([]); // false
isPlainObject(new Date()); // false

isArray([]); // true
isArray('string'); // false

isFunction(() => {}); // true
isFunction('string'); // false

Import Strategies

datype is optimized for tree-shaking. Choose the import style that fits your needs:

// 1. Named imports (recommended for most projects)
import { deepMerge, pick, debounce } from 'datype';

// 2. Individual imports (maximum tree-shaking)
import { deepMerge } from 'datype/deepMerge';

// 3. Wildcard import (when using many functions)
import * as dt from 'datype';
const result = dt.deepMerge(obj1, obj2);

TypeScript Configuration

datype uses pragmatic TypeScript types that prioritize developer experience. For optimal type inference, ensure your tsconfig.json includes:

{
  "compilerOptions": {
    "strict": true,
    "exactOptionalPropertyTypes": true,
    "noUncheckedIndexedAccess": true
  }
}

Philosophy: We believe TypeScript should enhance productivity, not hinder it. Our types are designed to be permissive where needed while maintaining essential type safety.

Bundle Size

datype is designed for optimal bundle size. Import only what you need:

| Import | Bundle Impact | |--------|--------------| | import { pick } | ~200 bytes | | import { deepMerge } | ~800 bytes | | import { debounce, throttle } | ~600 bytes | | Full library | ~5KB gzipped |

Browser Support

  • Modern browsers: Chrome 80+, Firefox 72+, Safari 13+, Edge 80+
  • Node.js: 14.0+
  • TypeScript: 4.5+

Contributing

We welcome contributions! Please read our Contributing Guide for:

  • Development setup
  • Code standards
  • Testing requirements
  • Pull request process

License

MIT © Etienne Brun


Built with ❤️ for the TypeScript community

Report BugRequest FeatureDocumentation