
Product
Introducing Rust Support in Socket
Socket now supports Rust and Cargo, offering package search for all users and experimental SBOM generation for enterprise projects.
ts-data-forge
Advanced tools
[](https://www.npmjs.com/package/ts-data-forge) [](https://www.npmjs.com/package/ts-data-forge) [, set (ISet
) implementations.pipe
, Optional
, and Result
to support functional programming patterns.npm install ts-data-forge
Or with other package managers:
# Yarn
yarn add ts-data-forge
# pnpm
pnpm add ts-data-forge
ts-data-forge works best with strict TypeScript settings:
{
"compilerOptions": {
"strict": true, // important
"noUncheckedIndexedAccess": true, // important
"noPropertyAccessFromIndexSignature": true, // important
"noFallthroughCasesInSwitch": true,
"noImplicitOverride": true,
"noImplicitReturns": true,
"noUnusedLocals": true,
"noUnusedParameters": true,
"allowUnreachableCode": false,
"allowUnusedLabels": false,
"exactOptionalPropertyTypes": false
}
}
Essential FP utilities for cleaner, more reliable code.
Runtime type checking with TypeScript integration.
isString
, isNumber
, isNonNullish
, etc.isRecord
, isNonNullObject
, hasKey
isNonEmptyString
, isPrimitive
Branded number types and safe arithmetic operations.
Int
, Uint
, SafeInt
, FiniteNumber
Int16
, Uint32
, PositiveInt
, etc.Type-safe array and tuple utilities with functional programming patterns.
Immutable data structures for safer state management.
And mutable Queue/Stack implementation
Additional helpers for common programming tasks.
castMutable
, castReadonly
memoizeFunction
, mapNullable
, unknownToString
ifThen
for conditional operationsHere are some examples of how to use utilities from ts-data-forge
:
expectType
The expectType
utility allows you to make assertions about types at compile time. This is useful for ensuring type correctness in complex type manipulations or when refactoring.
import { expectType } from 'ts-data-forge';
type User = { id: number; name: string };
type Admin = { id: number; name: string; role: 'admin' };
// Assert that Admin extends User
expectType<Admin, User>('<=');
// Assert that User does not extend Admin
expectType<User, Admin>('!<=');
// Assert exact type equality
expectType<{ x: number }, { x: number }>('=');
// The following would cause a compile-time error:
// expectType<User, Admin>("="); // Error: Type 'User' is not strictly equal to type 'Admin'.
// eslint-disable-next-line @typescript-eslint/no-explicit-any
expectType<User, any>('!='); // Error: Comparisons with `any` are also strictly checked.
Optional
, Result
, pipe
, and match
Handle nullable values and error-prone operations safely.
import { match, Optional, pipe, Result } from 'ts-data-forge';
// Optional for nullable values
const maybeValue = Optional.some(42);
const doubled = Optional.map(maybeValue, (x) => x * 2);
assert(Optional.unwrapOr(doubled, 0) === 84);
// Result for error handling
const success = Result.ok(42);
const mapped = Result.map(success, (x) => x * 2);
assert.deepStrictEqual(mapped, Result.ok(84));
// Advanced pipe usage
const processNumber = (input: number): Optional<number> =>
pipe(input)
.map((x) => x * 2) // Regular transformation
.map((x) => x + 10) // Chain transformations
.map((x) => (x > 50 ? Optional.some(x / 2) : Optional.none)).value; // Get the result
assert.deepStrictEqual(processNumber(30), Optional.some(35));
assert.deepStrictEqual(processNumber(10), Optional.none);
// Pattern matching with match
type Status = 'loading' | 'success' | 'error';
const handleStatus = (status: Status, data?: string): string =>
match(status, {
loading: 'Please wait...',
success: `Data: ${data ?? 'No data'}`,
error: 'An error occurred',
});
assert(handleStatus('loading') === 'Please wait...');
assert(handleStatus('success', 'Hello') === 'Data: Hello');
assert(handleStatus('error') === 'An error occurred');
// Pattern matching with Result
const processResult = (result: Result<number, string>): string =>
Result.isOk(result) ? `Success: ${result.value}` : `Error: ${result.value}`;
assert(processResult(Result.ok(42)) === 'Success: 42');
assert(processResult(Result.err('Failed')) === 'Error: Failed');
Num
and Branded Number TypesThe Num
object provides safe and convenient functions for numerical operations.
import { Num } from 'ts-data-forge';
// Basic conversions
assert(Num.from('123') === 123);
assert(Number.isNaN(Num.from('abc')));
// Range checking
const inRange = Num.isInRange(0, 10);
assert(inRange(5));
assert(inRange(0)); // (inclusive lower bound)
assert(!inRange(10)); // (exclusive upper bound)
// Clamping values
const clamp = Num.clamp(0, 100);
assert(clamp(150) === 100);
assert(clamp(-10) === 0);
// Rounding utilities
const round2 = Num.round(2);
assert(round2(3.14159) === 3.14);
assert(Num.roundAt(3.14159, 3) === 3.142);
assert(Num.roundToInt(3.7) === 4);
// Type guards
const value = 5; // example value
if (Num.isNonZero(value)) {
// value is guaranteed to be non-zero
const result = Num.div(10, value); // Safe division
assert(result === 2);
}
ts-data-forge
provides branded number types that enforce specific constraints at the type level.
import {
asFiniteNumber,
asInt,
asInt16,
asNonZeroInt,
asPositiveInt,
asSafeInt,
asUint,
asUint32,
Int16,
NonZeroInt,
} from 'ts-data-forge';
// Basic branded types
const integer = asInt(42); // Int - any integer
const unsigned = asUint(42); // Uint - non-negative integer
const finite = asFiniteNumber(3.14); // FiniteNumber - finite floating-point
const safeInt = asSafeInt(42); // SafeInt - integer in safe range
assert(integer === 42);
assert(unsigned === 42);
assert(finite === 3.14);
assert(safeInt === 42);
// This line would cause a runtime error:
assert.throw(() => {
asInt(3.14);
});
// Range-constrained types (16-bit, 32-bit)
const int16 = asInt16(1000); // Int16: [-32768, 32767]
const uint32 = asUint32(3000000000); // Uint32: [0, 4294967295]
assert(int16 === 1000);
assert(uint32 === 3000000000);
// Non-zero and positive variants
const nonZeroInt = asNonZeroInt(5); // NonZeroInt - excludes zero
const positiveInt = asPositiveInt(10); // PositiveInt - excludes zero and negatives
assert(nonZeroInt === 5);
assert(positiveInt === 10);
// Type-safe arithmetic with automatic clamping
const sum = Int16.add(int16, asInt16(2000)); // Int16 (3000)
const clamped = Int16.clamp(100000); // Int16 (32767 - clamped to MAX_VALUE)
assert(sum === 3000);
assert(clamped === 32767);
// Safe division with non-zero types
const ratio = NonZeroInt.div(asNonZeroInt(10), nonZeroInt); // No division by zero risk
assert(ratio === 2);
// Random generation within type constraints
const randomInt16 = Int16.random(); // Int16 (random value in valid range)
assert(-32768 <= randomInt16);
assert(randomInt16 <= 32767);
Arr
The Arr
object provides a rich set of functions for array manipulation.
import { Arr, expectType, Optional } from 'ts-data-forge';
const numbers: readonly number[] = [1, 2, 3, 4, 5, 2, 3];
// Reduction
const sum = Arr.sum(numbers);
assert(sum === 20);
// Type-safe length checking
if (Arr.isArrayAtLeastLength(numbers, 2)) {
// numbers is now guaranteed to have at least 2 elements
expectType<typeof numbers, readonly [number, number, ...number[]]>('=');
assert(numbers[1] === 2); // Safe access to index 1
}
// Take first n elements
const firstThree = Arr.take(numbers, 3);
assert.deepStrictEqual(firstThree, [1, 2, 3]);
// Remove duplicates
const unique = Arr.uniq(numbers);
assert.deepStrictEqual(unique, [1, 2, 3, 4, 5]);
// Array creation
const zeros: readonly [0, 0, 0, 0, 0] = Arr.zeros(5);
assert.deepStrictEqual(zeros, [0, 0, 0, 0, 0]);
const range: readonly [1, 2, 3] = Arr.range(1, 4);
assert.deepStrictEqual(range, [1, 2, 3]);
const people = [
{ name: 'Alice', age: 30 },
{ name: 'Bob', age: 25 },
{ name: 'Charlie', age: 35 },
] as const;
// Find maximum by property
const oldestPerson = Arr.maxBy(people, (person) => person.age);
assert.deepStrictEqual(
oldestPerson,
Optional.some({ name: 'Charlie', age: 35 } as const),
);
if (Optional.isSome(oldestPerson)) {
assert(oldestPerson.value.name === 'Charlie');
}
IMap
and ISet
Type-safe, immutable data structures.
import { Arr, IMap, ISet, Optional } from 'ts-data-forge';
// IMap usage - immutable operations
const originalMap = IMap.create<string, number>([]);
const mapWithOne = originalMap.set('one', 1);
const mapWithTwo = mapWithOne.set('two', 2);
// Original map is unchanged
assert(originalMap.size === 0);
assert.deepStrictEqual(mapWithTwo.get('one'), Optional.some(1));
assert(!mapWithTwo.has('three'));
// Using pipe for fluent updates
const sequence = Arr.seq(10); // [0, 1, 2, ..., 9]
const pairs = sequence.map(
(i) => [i, i.toString()] as readonly [number, string],
);
const skipped = Arr.skip(pairs, 1); // [[1, "1"], ..., [9, "9"]]
const idMap = IMap.create<number, string>(skipped);
assert(idMap.size === 9);
// Efficient batch updates with withMutations
const idMapUpdated = idMap.withMutations([
{ type: 'set', key: 99, value: '99' },
{ type: 'update', key: 5, updater: () => 'five' },
{ type: 'delete', key: 4 },
]);
assert(idMapUpdated.size === 9);
// ISet usage
const originalSet = ISet.create<number>([]);
const setWithItems = originalSet.add(1).add(2).add(1); // Duplicate ignored
assert(originalSet.size === 0); // (unchanged)
assert(setWithItems.has(1));
assert(setWithItems.size === 2);
Safe type narrowing with comprehensive type guards.
import { hasKey, isNonNullObject, isRecord } from 'ts-data-forge';
const processData = (data: unknown): string | undefined => {
if (
isRecord(data) && // data is now UnknownRecord (= Readonly<Record<string, unknown>>)
hasKey(data, 'name') &&
// data is now ReadonlyRecord<"name", unknown> & UnknownRecord
typeof data.name === 'string'
) {
return `Hello, ${data.name}!`;
}
return undefined;
};
// Non-null object checking
const value: unknown = { key: 'value' };
if (isNonNullObject(value)) {
// value is guaranteed to be a non-null object
assert.deepStrictEqual(Object.keys(value), ['key']);
}
// Example usage
assert(processData({ name: 'Alice' }) === 'Hello, Alice!');
assert(processData({ age: 30 }) === undefined);
assert(processData('not an object') === undefined);
range
Generate ranges for iteration and array creation.
import { range } from 'ts-data-forge';
// Traditional for loop using range
const mut_values: number[] = [];
for (const i of range(0, 5)) {
mut_values.push(i);
}
assert.deepStrictEqual(mut_values, [0, 1, 2, 3, 4]);
// Create arrays from ranges
const numbers = Array.from(range(1, 4));
const squares = Array.from(range(1, 6), (x) => x * x);
assert.deepStrictEqual(numbers, [1, 2, 3]);
assert.deepStrictEqual(squares, [1, 4, 9, 16, 25]);
// Step ranges
const mut_stepValues: number[] = [];
for (const i of range(0, 10, 2)) {
mut_stepValues.push(i);
}
assert.deepStrictEqual(mut_stepValues, [0, 2, 4, 6, 8]);
castMutable
Safely work with readonly types when interfacing with mutable APIs.
import { castMutable } from 'ts-data-forge';
// Example: Material-UI Autocomplete
import { Autocomplete, TextField } from '@mui/material';
export const SomeComponent: React.FC = () => (
<Autocomplete
options={castMutable(readonlyOptions)}
renderInput={(params) => (
<TextField {...params} placeholder="Select an option" />
)}
/>
);
const readonlyOptions: readonly string[] = ['Option 1', 'Option 2', 'Option 3'];
// Immer.js example
import { produce } from 'immer';
type State = Readonly<{
items: readonly string[];
}>;
const initialState: State = {
items: ['item1', 'item2'],
} as const;
const newItems: readonly string[] = ['newItem1', 'newItem2'];
const updatedState = produce(initialState, (draft) => {
// draft.items expects mutable array, but newItems is readonly
draft.items = castMutable(newItems); // Safe cast for assignment
});
assert.deepStrictEqual(initialState.items, ['item1', 'item2']);
assert.deepStrictEqual(updatedState.items, ['newItem1', 'newItem2']);
expect-type
: Compile-time type assertion utilities for testing and verification.guard
: Type guard functions for safe type narrowing (e.g., isNonNullObject
, isRecord
).functional
: Functional programming helpers like Optional
, Result
, pipe
, and match
.number
: Comprehensive numerical utilities including the Num
namespace and an extensive collection of branded number types (Int
, Uint
, SafeInt
, Int16
, Int32
, Uint16
, Uint32
, NonZeroInt
, PositiveInt
, NonNegativeFiniteNumber
, etc.) with type-safe arithmetic operations, range checking, and automatic clamping.array
: Utilities for working with arrays and tuples, including creation, transformation, and type-safe operations.object
: Utilities for working with records/objects (e.g., Obj.shallowEq
).json
: Type-safe JSON parsing and stringification utilities.collections
: Immutable data structures like IMap
, ISet
, and Queue
with full type safety.iterator
: Utilities for working with iterators and generators (e.g., range
).others
: Miscellaneous utilities like castMutable
, castReadonly
, ifThen
, mapNullable
, memoizeFunction
, tuple
, unknownToString
.Optional
, Result
, and pipe
.Important Notes:
.mts
file extensions and proper ESM exports, making it compatible with modern Node.js ESM resolution and bundlers that support native ESM.Uint32
in newArray
). The examples above use the small literal numeric values specifically allowed in each function for brevity, but in actual use you should use the provided type conversion functions (such as asUint32
) or cast to the appropriate branded type, for example as Uint32
.expectType
in ProductionSince expectType
is only used for compile-time type checking, you should remove these calls in production builds for better performance.
import rollupPluginStrip from '@rollup/plugin-strip';
export default {
// ... other config
plugins: [
// ... other plugins
rollupPluginStrip({
functions: ['expectType'],
include: '**/*.(mts|ts|mjs|js)',
}),
],
};
import { defineConfig } from 'vite';
export default defineConfig({
// ... other config
build: {
terserOptions: {
compress: {
pure_funcs: ['expectType'],
},
},
},
});
Contributions are welcome! Please see CONTRIBUTING.md for detailed guidelines on how to contribute to this project.
This project is licensed under the Apache License 2.0.
FAQs
[](https://www.npmjs.com/package/ts-data-forge) [](https://www.npmjs.com/package/ts-data-forge) [![License](https://img.shields.
We found that ts-data-forge demonstrated a healthy version release cadence and project activity because the last version was released less than a year ago. It has 0 open source maintainers collaborating on the project.
Did you know?
Socket for GitHub automatically highlights issues in each pull request and monitors the health of all your open source dependencies. Discover the contents of your packages and block harmful activity before you install or update your dependencies.
Product
Socket now supports Rust and Cargo, offering package search for all users and experimental SBOM generation for enterprise projects.
Product
Socket’s precomputed reachability slashes false positives by flagging up to 80% of vulnerabilities as irrelevant, with no setup and instant results.
Product
Socket is launching experimental protection for Chrome extensions, scanning for malware and risky permissions to prevent silent supply chain attacks.