New Functions
Add assert
function → PR #403
The assert
function from Radashi is used to assert that a given condition
is true. If the condition
evaluates to false
, the function throws an error. This is a fundamental building block for ensuring that certain conditions are met at runtime. This utility is particularly useful in TypeScript for its ability to perform type narrowing.
- Asserts a condition and throws an error if false.
- Useful for TypeScript type narrowing using the
asserts
keyword. - Accepts an optional message (string or Error instance) for failed assertions.
assert(false, ...)
has anever
return type for unreachable code paths.- Inspired by Node.js's
assert
module.
import * as _ from 'radashi'
function processValue(value: string | null | undefined) {
_.assert(value, 'Value cannot be null, undefined, or empty')
// After the assertion, 'value' is narrowed to type 'string'
console.log(value.toUpperCase())
}
processValue('hello') // logs "HELLO"
// _.assert throws on falsy values like:
// - null
// - undefined
// - '' (empty string)
// - 0
// - false
Add escapeHTML
function → PR #401
Replaces all occurrences of specific characters with their corresponding HTML entities to escape HTML in a string.
&
is replaced with&
<
is replaced with<
>
is replaced with>
"
is replaced with"
'
is replaced with'
import * as _ from 'radashi'
_.escapeHTML(`Sarah said, "5 < 10 & that's obvious."`)
// => 'Sarah said, "5 < 10 & that's obvious."'
Add parseDuration
function → PR #416
Parses a human-readable duration string (like "1 hour", "2 seconds") into milliseconds.
- Supports units like millisecond, second, minute, hour, day, and week.
- Custom units can be added.
- A
DurationParser
class is available for more efficient repeated parsing.
import * as _ from 'radashi'
_.parseDuration('1 second') // => 1_000
_.parseDuration('1h') // => 3_600_000
_.parseDuration('1 hour') // => 3_600_000
_.parseDuration('1.5 hours') // => 5_400_000
_.parseDuration('-1h') // => -3_600_000
Thanks to Alec Larson and @hugo082 for their work on this feature!
Add parseQuantity
function → PR #416
Parses a quantity string like "2 dollars"
into its numeric value. You must provide a unit conversion map, with optional short unit aliases.
- Requires a unit conversion map.
- Supports optional short unit aliases.
- A
QuantityParser
class is available for more efficient repeated parsing and subclassing.
import * as _ from 'radashi'
const moneyUnits = {
units: {
cent: 1,
dollar: 100,
},
short: {
$: 'dollar',
},
} as const
_.parseQuantity('1 cent', moneyUnits)
// => 1
_.parseQuantity('2 dollars', moneyUnits)
// => 200
_.parseQuantity('5$', moneyUnits)
// => 500
Thanks to Alec Larson and @hugo082 for their work on this feature!
Add promiseChain
function → PR #402
Chain together multiple, potentially asynchronous functions. The result of each function is passed to the next function.
- Executes functions in the order they are provided.
- Supports both synchronous and asynchronous functions.
- Returns a Promise with the final result.
import * as _ from 'radashi'
const func1 = (a, b) => a + b
const func2 = async n => n * 2
const func3 = async n => `Your Value is ${n}`
const chained = _.promiseChain(func1, func2, func3)
await chained(5, 2) // => "Your Value is 14"
Thanks to Bharat Soni for their work on this feature!
Add queueByKey
function → PR #407
Wraps an asynchronous function to ensure that calls with the same key are queued and executed sequentially, while calls with different keys can run in parallel. This is useful for preventing race conditions when operations must not overlap for the same logical group (like user ID or resource ID).
- Sequential per key: Operations with the same key execute one after another
- Parallel across keys: Operations with different keys run concurrently
- Error handling: Errors are properly propagated and don't break the queue
- Memory efficient: Queues are automatically cleaned up when empty
- Type safe: Full TypeScript support with generic types
import * as _ from 'radashi'
const updateUser = async (userId: string, data: object) => {
// Simulate API call that shouldn't overlap for the same user
const response = await fetch(`/api/users/${userId}`, {
method: 'POST',
body: JSON.stringify(data),
})
return response.json()
}
const queuedUpdate = _.queueByKey(updateUser, userId => userId)
// These will run sequentially for user123
queuedUpdate('user123', { name: 'Alice' })
queuedUpdate('user123', { age: 30 })
// This runs in parallel with user123's queue
queuedUpdate('user456', { name: 'Bob' })
Add Semaphore
class → PR #415
A synchronization primitive that allows a limited number of concurrent operations to proceed.
- Limits the number of concurrent operations.
- Use
acquire()
to get a permit andrelease()
to free it. - Supports acquiring permits with a specific weight.
- Pending acquisitions can be aborted using
AbortController
. - All pending and future acquisitions can be rejected using
semaphore.reject()
.
import { Semaphore } from 'radashi'
const semaphore = new Semaphore(2)
const permit = await semaphore.acquire()
permit.release()
Thanks to Alec Larson and @hugo082 for their work on this feature!
New Features
Pass array index to group callback → Commit 6d66395
The callback function provided to the group
function now receives the array index as its second argument. This allows for more flexible grouping logic that can take into account the position of elements in the original array.
- The callback signature is now
(item, index) => groupKey
. - Enables grouping based on element position as well as value.
import * as _ from 'radashi'
const items = ['a', 'b', 'c', 'd', 'e']
const groupedByIndex = _.group(items, (item, index) =>
index % 2 === 0 ? 'even' : 'odd',
)
// => { even: ['a', 'c', 'e'], odd: ['b', 'd'] }
Fixes
- Fix incorrect type narrowing in
isIntString
(→ PR #412) by Igor Golovin - Fix filtering out null from
selectFirst
return type when no condition is given (→ PR #413) by Ali Medhat