Skip to content

Latest commit

 

History

History

utilities

@flowr/utilities

Opinionated collection of common TypeScript utilities

Installation

You can use the following command to install this package, or replace pnpm add with your package manager of choice.

pnpm add -D @flowr/utilities

Usage

You can import individual utility function from subpath like: @flowr/utilities/isFunction or the entire library.

import { isFunction } from '@flowr/utilities';
/* alternatively... */
import { isFunction } from '@flowr/utilities/isFunction';

Documentation

arrayStrictEquals

Compares if two arrays are strictly equal.

arrayStrictEquals([1, 2, 3], [1, 2, 3]); // true
arrayStrictEquals([1, 2, 3], [1, 2, 3, 4]); // false
arrayStrictEquals([1, 2, 3], [1, 2, 4]); // false

asyncQueue

Sequential asynchronous lock-based queue for promises.

const queue = new AsyncQueue();

async function request(url, options) {
	// Wait and lock the queue
	await queue.wait();

	try {
		// Perform the operation sequentially
		return await fetch(url, options);
	}
	finally {
		// Unlock the next promise in the queue
		queue.shift();
	}
}

request(someUrl1, someOptions1);
// Will call fetch() immediately

request(someUrl2, someOptions2);
// Will call fetch() after the first finished

request(someUrl3, someOptions3);
// Will call fetch() after the second finished

chunk

Splits up an array into chunks.

chunk([1, 2, 3, 4, 5], 2); // [[1, 2], [3, 4], [5]]
chunk([1, 2, 3, 4, 5], 3); // [[1, 2, 3], [4, 5]]

classExtends

Checks whether or not the value class extends the base class.

class A {}
class B extends A {}

classExtends(A, B); // false
classExtends(B, A); // true

codeBlock

Wraps text in a markdown codeblock with a language indicator for syntax highlighting.

codeBlock('js', 'const value = "Hello World!";'); // ```js\nconst value = "Hello World!";\n```

cutText

Split a text by its latest space character in a range from the character 0 to the selected one.

cutText('Lorem Ipsum', 9); // "Lorem..."

deepClone

Deep clones an object.

const obj = { a: 1, b: { c: 2 } };
const clone = deepClone(obj); // { a: 1, b: { c: 2 } }

duration

Duration management utilities.

Human Readable Milliseconds

Milliseconds are often hard for humans to quickly parse, so it's nice to use named enum members to make it easier to read.

setTimeout(() => {
	// Do something in half a second
}, Time.Second / 2 /* 500 */);

setTimeout(() => {
	// Do something in 6 hours
}, Time.Hour * 6 /* 21600000 */);

setTimeout(() => {
	// Do something in 1 day
}, Time.Day /* 86400000 */);
Parsing a Duration
// Create a Duration from a string
new Duration('1d3h15m3s').offset; // 98103000
new Duration('1 day, 3h & 15m, some extra characters, and another 3 seconds').offset; // 98103000

// The date from now after the specified duration
new Duration('1d3h15m3s').fromNow;

// Or use a specific date
new Duration('1d3h15m3s').dateFrom(new Date('2020-01-01T00:00:00.000Z'));
Show all available tokens
new Duration('1 nanosecond').offset; // 0.000001
new Duration('2 nanoseconds').offset; // 0.000002
new Duration('1 ns').offset; // 0.000001

new Duration('1 millisecond').offset; // 1
new Duration('2 milliseconds').offset; // 2
new Duration('1 ms').offset; // 1

new Duration('1 second').offset; // 1000
new Duration('2 seconds').offset; // 2000
new Duration('1 sec').offset; // 1000
new Duration('2 secs').offset; // 2000
new Duration('1 s').offset; // 1000

new Duration('1 minute').offset; // 60000
new Duration('2 minutes').offset; // 120000
new Duration('1 min').offset; // 60000
new Duration('2 mins').offset; // 120000
new Duration('1 m').offset; // 60000

new Duration('1 hour').offset; // 3600000
new Duration('2 hours').offset; // 7200000
new Duration('1 hr').offset; // 3600000
new Duration('2 hrs').offset; // 7200000
new Duration('1 h').offset; // 3600000

new Duration('1 day').offset; // 86400000
new Duration('2 days').offset; // 172800000
new Duration('1 d').offset; // 86400000

new Duration('1 week').offset; // 604800000
new Duration('2 weeks').offset; // 1209600000
new Duration('1 wk').offset; // 604800000
new Duration('2 wks').offset; // 1209600000
new Duration('1 w').offset; // 604800000

new Duration('1 month').offset; // 2629800000
new Duration('2 months').offset; // 5259600000
new Duration('1 b').offset; // 2629800000
new Duration('2 mo').offset; // 5259600000

new Duration('1 year').offset; // 31557600000
new Duration('2 years').offset; // 63115200000
new Duration('1 yr').offset; // 31557600000
new Duration('2 yrs').offset; // 63115200000
new Duration('1 y').offset; // 31557600000
Serializing a Duration
const formatter = new DurationFormatter();

// Serialize a duration
formatter.format(98103000); // 1 day 3 hours 15 minutes 3 seconds
formatter.format(-98103000); // -1 day 3 hours 15 minutes 3 seconds

// Serialize a duration with specified precision
formatter.format(98103000, 2); // 1 day 3 hours
Localizing with Durations
// Create custom unit names
const units = {
	[TimeTypes.Year]: {
		1: 'año',
		DEFAULT: 'años'
	}
};

// Create a formatter the custom units
const formatter = new DurationFormatter(units);

// Serialize a duration
formatter.format(31557600000); // 1 año
formatter.format(63115200000); // 2 años

filterNullAndUndefined

Checks whether a value is not null nor undefined. This can be used in Array#filter to remove null and undefined from the array type

// TypeScript Type: (string | undefined | null)[]
const someArray = ['one', 'two', undefined, null, 'five'];

// TypeScript Type: string[]
const filteredArray = someArray.filter(filterNullAndUndefined); // ['one', 'two', 'five']

filterNullAndUndefinedAndEmpty

Checks whether a value is not null, undefined, or '' (empty string). This can be used in Array#filter to remove null, undefined, and '' from the array type

// TypeScript Type: (number | string | undefined | null)[]
const someArray = [1, 2, undefined, null, ''];

// TypeScript Type: number[]
const filteredArray = someArray.filter(filterNullAndUndefinedAndEmpty); // [1, 2]

filterNullAndUndefinedAndZero

Checks whether a value is not null, undefined, or 0. This can be used in Array#filter to remove null, undefined, and 0 from the array type

// TypeScript Type: (string | number | undefined | null)[]
const someArray = ['one', 'two', undefined, null, 0];

// TypeScript Type: string[]
const filteredArray = someArray.filter(filterNullAndUndefinedAndZero); // ['one', 'two']

getDeepObjectKeys

Returns an array of all the keys of an object, including the keys of nested objects.

const obj = { a: 1, b: { c: 2 }, d: [{ e: 3 }] };
getDeepObjectKeys(obj); // ['a', 'b.c', 'd.0.e']
getDeepObjectKeys(obj, { arrayKeysIndexStyle: 'braces' }); // ['a', 'bc', 'd[0]e']
getDeepObjectKeys(obj, { arrayKeysIndexStyle: 'braces-with-dot' }); // ['a', 'b.c', 'd[0].e']
getDeepObjectKeys(obj, { arrayKeysIndexStyle: 'dotted' }); // ['a', 'b.c', 'd.0.e']

hasAtLeastOneKeyInMap

Checks whether a map has at least one of an array of keys.

const map = new Map([
	['a', 1],
	['b', 2],
	['c', 3]
]);

hasAtLeastOneKeyInMap(map, ['a', 'd']); // true
hasAtLeastOneKeyInMap(map, ['d', 'e']); // false

inlineCodeBlock

Wraps text in a markdown inline codeblock.

inlineCodeBlock('const value = "Hello World!";'); // `const value = "Hello World!";`

isClass

Verifies if the input is a class constructor.

class A {}

isClass(A); // true
isClass(() => {}); // false

isFunction

Verifies if the input is a function.

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

isNullOrUndefined

Checks whether a value is null or undefined.

isNullOrUndefined(null); // true
isNullOrUndefined(undefined); // true
isNullOrUndefined(1); // false

isNullOrUndefinedOrEmpty

Checks whether a value is null, undefined, or '' (empty string).

isNullOrUndefinedOrEmpty(null); // true
isNullOrUndefinedOrEmpty(undefined); // true
isNullOrUndefinedOrEmpty(''); // true
isNullOrUndefinedOrEmpty(1); // false

isNullOrUndefinedOrZero

Checks whether a value is null, undefined, or 0.

isNullOrUndefinedOrZero(null); // true
isNullOrUndefinedOrZero(undefined); // true
isNullOrUndefinedOrZero(0); // true
isNullOrUndefinedOrZero(1); // false

isNumber

Verifies if the input is a number.

isNumber(1); // true
isNumber('1'); // false

isObject

Verifies if the input is an object.

isObject({}); // true
isObject([]); // true
isObject('foo'); // false

isPrimitive

Verifies if the input is a primitive.

isPrimitive(1); // true
isPrimitive('1'); // true
isPrimitive({}); // false

isThenable

Verifies if an object is a promise.

isThenable({}); // false
isThenable(Promise.resolve()); // true

lazy

Lazily creates a constant or load a module and caches it internally.

let timesCalled = 0;
const lazyValue = lazy(() => {
	timesCalled++;
	return 'foo';
});

lazyValue(); // 'foo'
lazyValue(); // 'foo' - cached

timesCalled; // 1

makeObject

Turn a dotted path into a json object.

makeObject('a.b.c', 1); // { a: { b: { c: 1 } } }

mergeDefault

Deep merges two objects. Properties from the second parameter are applied to the first.

const base = { a: 1, b: { c: 2 } };
const overwritten = { b: { d: 3 } };

mergeDefault(base, overwritten);
overwritten; // { a: 1, b: { c: 2, d: 3 } }

mergeObjects

Merges two objects.

const source = { a: 1, b: 2 };
const target = { c: 4 };

mergeObjects(source, target);
target; // { a: 1, b: 2, c: 4 }

noop

A no-operation function.

noop(); // undefined

// Example usage of ignoring a promise rejection
Promise.reject(new Error('noop')).catch(noop);

objectToTuples

Converts an object to a tuple with string paths.

const obj = { a: 1, b: { c: 2 } };
objectToTuples(obj); // [['a', 1], ['b.c', 2]]

partition

Partitions an array into a tuple of two arrays, where one array contains all elements that satisfies the predicate, and the other contains all elements that do not satisfy the predicate.

const arr = [1, 2, 3, 4, 5];
const [evens, odds] = partition(arr, n => n % 2 === 0);

evens; // [2, 4]
odds; // [1, 3, 5]

pickRandom

Picks a random element from an array.

const arr = [1, 2, 3, 4, 5];
pickRandom(arr); // 3

range

Get an array of numbers with the selected range, considering a specified step.

range(1, 4, 1); // [1, 2, 3, 4]
range(1, 4, 2); // [1, 3]
range(4, 1, -1); // [4, 3, 2, 1]
range(4, 1, -2); // [4, 2]

regExpEsc

Cleans a string from regex injection by escaping special characters.

regExpEsc('foo.bar?'); // 'foo\\.bar\\?'

roundNumber

Properly rounds up or down a number. Also supports strings using an exponent to indicate large or small numbers.

roundNumber(1.9134658034); // 1
roundNumber(1.9134658034, 2); // 1.91
roundNumber('10e-5'); // 0

sleep / sleepSync

Sleeps for the specified number of milliseconds.

await sleep(1000); // Sleeps for 1 second
sleepSync(1000); // Sleeps for 1 second

splitText

Split a string by its latest space character in a range from the character 0 to the selected one.

splitText('Hello All People!', 8); // 'Hello'
splitText('Hello All People!', 10); // 'Hello All'

stopwatch

A set of methods and properties used to accurately measure elapsed time.

// Create a new Stopwatch (which also starts it immediately)
const stopwatch = new Stopwatch();

// run other task here

console.log(stopwatch.stop().toString());
// 200ms

throttle

Creates a throttled function that only invokes a function at most once per every x milliseconds. The throttled function comes with a flush method to reset the last time the throttled function was invoked.

const throttled = throttle(() => console.log('throttled'), 1000);

throttled(); // 'throttled'
throttled(); // nothing
throttled.flush();
throttled(); // 'throttled'

timerManager

timer management utilities

setTimeout(() => {
	// Do something in half a second
}, Time.Second / 2 /* 500 */);

setTimeout(() => {
	// Do something in 6 hours
}, Time.Hour * 6 /* 21600000 */);

setTimeout(() => {
	// Do something in 1 day
}, Time.Day /* 86400000 */);

// Use the class for timeouts
const timeout = TimerManager.setTimeout(() => console.log('Hello, world!'), 1000);
TimerManager.clearTimeout(timeout);

// Use the class for intervals
const interval = TimerManager.setInterval(() => console.log('Hello, world!'), 1000);
TimerManager.clearInterval(interval);

// Destroy all running timeouts and intervals so that NodeJS can gracefully exit
TimerManager.destroy();

timestamp

formats a timestamp with typescript, human-readable code

// Saturday 9th March 2019, at 16:20:35:500
const date = new Date(2019, 2, 9, 16, 20, 35, 1);

// Format the date with tokens (use square brackets to escape)
const timestamp = new Timestamp('MMMM d YYYY[, at ]HH:mm:ss:SSS');
timestamp.display(date); // March 9th 2019, at 16:20:35:001

// Saturday 9th March 2019, at 16:20:35:500
const date = new Date(2019, 2, 9, 16, 20, 35, 1);

new Timestamp('Y').display(date); // 19
new Timestamp('YY').display(date); // 19
new Timestamp('YYY').display(date); // 2019
new Timestamp('YYYY').display(date); // 2019
new Timestamp('Q').display(date); // 1
new Timestamp('M').display(date); // 3
new Timestamp('MM').display(date); // 03
new Timestamp('MMM').display(date); // March
new Timestamp('MMMM').display(date); // March
new Timestamp('D').display(date); // 9
new Timestamp('DD').display(date); // 09
new Timestamp('DDD').display(date); // 68
new Timestamp('DDDD').display(date); // 68
new Timestamp('d').display(date); // 9th
new Timestamp('dd').display(date); // Sa
new Timestamp('ddd').display(date); // Sat
new Timestamp('dddd').display(date); // Saturday
new Timestamp('X').display(date); // 1552168835
new Timestamp('x').display(date); // 1552168835001
new Timestamp('H').display(date); // 16
new Timestamp('HH').display(date); // 16
new Timestamp('h').display(date); // 4
new Timestamp('hh').display(date); // 04
new Timestamp('a').display(date); // pm
new Timestamp('A').display(date); // PM
new Timestamp('m').display(date); // 20
new Timestamp('mm').display(date); // 20
new Timestamp('s').display(date); // 35
new Timestamp('ss').display(date); // 35
new Timestamp('S').display(date); // 0
new Timestamp('SS').display(date); // 00
new Timestamp('SSS').display(date); // 001
new Timestamp('t').display(date); // 4:20:35 PM
new Timestamp('T').display(date); // 4:20 PM
new Timestamp('L').display(date); // 03/09/2019
new Timestamp('LL').display(date); // March 09, 2019
new Timestamp('LLL').display(date); // March 09, 2019 4:20 PM
new Timestamp('LLLL').display(date); // Saturday, March 09, 2019 4:20 PM
new Timestamp('l').display(date); // 3/9/2019
new Timestamp('ll').display(date); // Mar 09, 2019
new Timestamp('lll').display(date); // Mar 09, 2019 4:20 PM
new Timestamp('llll').display(date); // Sat, Mar 09, 2019 4:20 PM
new Timestamp('Z').display(date); // -05:00
new Timestamp('ZZ').display(date); // -05:00

toTitleCase

Converts a string to Title Case.

toTitleCase('foo bar'); // 'Foo Bar'
toTitleCase('textchannel'); // 'TextChannel'
toTitleCase('onetwo three', { onetwo: 'OneTwo' }); // OneTwo Three

cast

Casts any value to T. Note that this function is not type-safe, and may cause runtime errors if used incorrectly.

const value = cast<string>(1); // value is now of type string

objectEntries

A strongly-typed alternative to Object.entries.

const obj = { a: 1, b: 2 } as const;

const native = Object.entries(obj); // [string, number][]
const strict = objectEntries(obj); // [['a', 1], ['b', 2]]

objectKeys

A strongly-typed alternative to Object.keys.

const obj = { a: 1, b: 2 } as const;

const native = Object.keys(obj); // string[]
const strict = objectKeys(obj); // ['a', 'b']

objectValues

A strongly-typed alternative to Object.values.

const obj = { a: 1, b: 2 } as const;

const native = Object.values(obj); // number[]
const strict = objectValues(obj); // [1, 2]