@tienedev/datype
Version:
Modern TypeScript utility library with pragmatic typing and zero dependencies
536 lines (380 loc) • 14.8 kB
Markdown
<div align="center">
**A modern, TypeScript-first utility library with perfect type inference and zero dependencies.**
[](https://www.npmjs.com/package/datype)
[](https://www.typescriptlang.org/)
[](https://codecov.io/gh/tiene9/datype)
[](https://opensource.org/licenses/MIT)
[](https://bundlephobia.com/package/datype)
[](https://github.com/tiene9/datype)
[](
</div>
**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.
```typescript
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 }[]>
```
- 🎯 **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
```bash
npm install datype
```
```bash
yarn add datype
```
```bash
pnpm add datype
```
```typescript
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'
```
Deep merge objects with intelligent type inference and configurable array handling.
```typescript
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 }
```
Extract specific properties with perfect type safety.
```typescript
const user = { id: 1, name: 'Alice', email: 'alice@example.com', password: 'secret' };
const safeUser = pick(user, ['id', 'name', 'email']);
// Type: { id: number; name: string; email: string }
```
Exclude specific properties with type safety.
```typescript
const user = { id: 1, name: 'Alice', email: 'alice@example.com', password: 'secret' };
const publicUser = omit(user, ['password']);
// Type: { id: number; name: string; email: string }
```
Safe nested property access with dot notation.
```typescript
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
```
Immutably set nested properties with flexible return typing.
```typescript
const obj = { a: { b: 1 } };
const updated = set(obj, 'a.c', 2);
// { a: { b: 1, c: 2 } }
// Original obj is unchanged
```
Transform all object values.
```typescript
const scores = { alice: '95', bob: '87', charlie: '92' };
const numeric = mapValues(scores, score => parseInt(score, 10));
// { alice: 95, bob: 87, charlie: 92 }
```
Transform all object keys with built-in transformers.
```typescript
import { mapKeys, keyTransformers } from 'datype';
const apiData = { 'user_name': 'Alice', 'user_email': 'alice@example.com' };
const camelCased = mapKeys(apiData, keyTransformers.camelCase);
// { userName: 'Alice', userEmail: 'alice@example.com' }
// Available transformers: camelCase, kebabCase, snakeCase, pascalCase, constantCase, dotCase
```
Split array into chunks of specified size.
```typescript
const numbers = [1, 2, 3, 4, 5, 6, 7, 8];
const chunked = chunk(numbers, 3);
// [[1, 2, 3], [4, 5, 6], [7, 8]]
```
Flatten nested arrays.
```typescript
const nested = [1, [2, 3], [4, [5, 6]]];
flatten(nested); // [1, 2, 3, 4, [5, 6]]
flattenDeep(nested); // [1, 2, 3, 4, 5, 6]
```
Remove duplicates.
```typescript
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' }]
```
Remove falsy values.
```typescript
const mixed = [1, null, 2, undefined, 3, false, 4, '', 5];
compact(mixed); // [1, 2, 3, 4, 5]
```
Delay function execution until calls stop.
```typescript
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
```
Limit function execution frequency.
```typescript
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);
```
Ensure function executes only once.
```typescript
const initialize = once(() => {
console.log('App initialized');
// expensive setup code
});
initialize(); // "App initialized"
initialize(); // Nothing happens
initialize(); // Nothing happens
```
Compose functions right-to-left.
```typescript
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
```
Compose functions left-to-right.
```typescript
const transform = pipe(addOne, double, square);
transform(3); // square(double(addOne(3))) = 64
```
Transform function to support flexible partial application with pragmatic typing.
```typescript
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
```
Check if value is empty.
```typescript
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
```
Deep equality comparison with circular reference support.
```typescript
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
```
Create deep clone with circular reference protection.
```typescript
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)
```
Convert text to URL-friendly slug.
```typescript
slugify('Hello World!'); // 'hello-world'
slugify('Café & Crème'); // 'cafe-and-creme'
slugify('TypeScript is Awesome!', {
separator: '_',
maxLength: 20
}); // 'typescript_is_awesome'
```
```typescript
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 first letter of each word.
```typescript
capitalize('hello world'); // 'Hello World'
capitalize('HELLO WORLD'); // 'Hello World'
capitalize('hello-world'); // 'Hello-World'
```
```typescript
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'
```
Group array elements.
```typescript
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] }
```
```typescript
import { isPlainObject, isArray, isFunction } from 'datype';
isPlainObject({}); // true
isPlainObject([]); // false
isPlainObject(new Date()); // false
isArray([]); // true
isArray('string'); // false
isFunction(() => {}); // true
isFunction('string'); // false
```
**datype** is optimized for tree-shaking. Choose the import style that fits your needs:
```typescript
// 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);
```
**datype** uses pragmatic TypeScript types that prioritize developer experience. For optimal type inference, ensure your `tsconfig.json` includes:
```json
{
"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.
**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 |
- **Modern browsers**: Chrome 80+, Firefox 72+, Safari 13+, Edge 80+
- **Node.js**: 14.0+
- **TypeScript**: 4.5+
We welcome contributions! Please read our [Contributing Guide](CONTRIBUTING.md) for:
- Development setup
- Code standards
- Testing requirements
- Pull request process
[](LICENSE) © [Etienne Brun](https://github.com/tiene9)
---
<div align="center">
**Built with ❤️ for the TypeScript community**
[](https://github.com/tiene9/datype/issues) • [Request Feature](https://github.com/tiene9/datype/issues) • [Documentation](https://github.com/tiene9/datype)
</div>