Starting today, you can install radashi@12.2.0
from NPM. This is the first stable release of Radashi, which we've been incubating for more than 4 months, starting on June 23.
Now, we look forward to merging more pull requests, attracting more users, and inspiring more talented folks to join us in building (and just as important, maintaining) the next-generation utility toolkit that is Radashi.
Lastly, I'd like to ask everyone to check out the proposed features open for discussion, and we hope you'll share with us any ideas you may have as well. Remember, this is a community effort. Your perspective is valuable and it will help us make Radashi better with your needs in mind.
New Functions
Add cloneDeep
function → PR #81
The cloneDeep
function creates a deep copy of an object or array.
- It supports cloning of plain objects, arrays,
Map
instances, andSet
instances by default. - The default behavior can be customized by providing a partial
CloningStrategy
implementation.
import * as _ from 'radashi'
const obj = { a: 1, b: { c: 2 } }
const clone = _.cloneDeep(obj)
// The clone and its nested objects have their own identity. Therefore,
// mutating them won't affect the original object, and vice versa.
assert(clone !== obj)
assert(clone.b !== obj.b)
assert(JSON.stringify(clone) === JSON.stringify(obj))
Add once
function → PR #80
Create a wrapper around a given function such that it executes at most once.
- Subsequent calls to the wrapped function return the result from the first execution, regardless of the arguments provided. This behavior is akin to “memoization” but specifically designed for single-use functions.
- Use
_.once.reset(fn)
to clear the stored result and allow the function to execute again.
import * as _ from 'radashi'
const fn = _.once(() => Math.random())
fn() // 0.5
fn() // 0.5
_.once.reset(fn)
fn() // 0.8
Cast a non-nullish value into an array → PR #97
The castArrayIfExists
function ensures that a non-nullish input value is always returned as an array.
- If the input is already an array, it returns a shallow copy of the array.
- If the input is not an array, it wraps the input in a new array.
- Nullish values (null or undefined) are passed through as is.
import * as _ from 'radashi'
_.castArrayIfExists(1) // => [1]
_.castArrayIfExists([1, 2, 3]) // => [1, 2, 3]
_.castArrayIfExists('hello') // => ['hello']
_.castArrayIfExists(null) // => null
_.castArrayIfExists(undefined) // => undefined
Cast a value into an array → PR #97
The castArray
function ensures that the input value is always returned as an array.
- If the input is already an array, it returns a shallow copy of the array.
- If the input is not an array, it wraps the input in a new array.
import * as _ from 'radashi'
_.castArray(1) // => [1]
_.castArray([1, 2, 3]) // => [1, 2, 3]
_.castArray('hello') // => ['hello']
Convert an array to a map → PR #58
The mapify
function allows you to convert an array into a Map
object, where the keys and values are determined by provided functions.
- The first callback determines the keys of the map.
- The second callback determines the values of the map. If not provided, the original array elements are used as values.
import * as _ from 'radashi'
const fish = [
{ name: 'Marlin', weight: 105 },
{ name: 'Bass', weight: 8 },
{ name: 'Trout', weight: 13 },
]
const fishByName = _.mapify(fish, f => f.name)
// => Map(3) {'Marlin' => { name: 'Marlin', weight: 105 }, 'Bass' => { name: 'Bass', weight: 8 }, 'Trout' => { name: 'Trout', weight: 13 }}
const fishWeightByName = _.mapify(
fish,
f => f.name,
f => f.weight,
)
// => Map(3) { 'Marlin' => 105, 'Bass' => 8, 'Trout' => 13 }
Round a number to a specified precision → PR #53
The round
function allows you to round a number to a specified precision.
- The precision can be a positive or negative integer.
- An optional rounding function (e.g.
Math.floor
orMath.ceil
) can be provided. The default rounding function isMath.round
.
import * as _ from 'radashi'
_.round(123.456) // => 123
_.round(1234.56, -2) // => 1200
_.round(4.001, 2, Math.ceil) // => 4.01
_.round(4.089, 2, Math.floor) // => 4.08
Allow deep traversal of objects and arrays → PR #59
The traverse
function recursively visits each property of an object (or each element of an array) and its nested objects or arrays.
- By default, the only nested objects to be traversed are plain objects and arrays.
- Traversal is performed in a depth-first manner, and circular references are skipped.
- The traversal can be customized with a
TraverseOptions
object.
import * as _ from 'radashi'
const root = { a: { b: 2 }, c: [1, 2] }
_.traverse(root, (value, key, parent, context) => {
const indent = ' '.repeat(context.parents.length)
console.log(`${indent}${key} => ${value}`)
})
// Logs the following:
// a => { b: 2 }
// b => 2
// c => [1, 2]
// 0 => 1
// 1 => 2
Calculate string similarity → PR #122
The similarity
function calculates the Levenshtein distance between two input strings, which represents the minimum number of single-character edits (insertions, deletions, or substitutions) required to change one string into the other.
- A lower number indicates higher similarity, with 0 meaning the strings are identical.
- The comparison is both case-sensitive and whitespace-significant.
- The argument order doesn't matter, as it's symmetric.
import * as _ from 'radashi'
// Identical strings
_.similarity('hello', 'hello') // => 0
// One character difference
_.similarity('kitten', 'sitten') // => 1
// Multiple differences
_.similarity('saturday', 'sunday') // => 3
Cast a value into a comparator function → PR #34
The castComparator
function allows you to create a comparator function that can be passed into Array.prototype.sort
.
Parameters:
mapping
is either a property name or a mapping function.compare
is an optional custom compare function (e.g. forlocaleCompare
use cases).reverse
reverses the comparison order if set totrue
.
import * as _ from 'radashi'
const users = [
{ id: 1, firstName: 'Alice', lastName: 'Smith' },
{ id: 3, firstName: 'Charlie', lastName: 'Brown' },
{ id: 2, firstName: 'Drew', lastName: 'Johnson' },
]
const compareById = _.castComparator('id')
users.sort(compareById)
// [Alice, Drew, Charlie]
const compareByFullName = _.castComparator(
user => `${user.firstName} ${user.lastName}`,
(a, b) => b.localeCompare(a),
)
users.sort(compareByFullName)
// [Alice, Charlie, Drew]
const compareByFullNameReversed = _.castComparator(
user => `${user.firstName} ${user.lastName}`,
(a, b) => b.localeCompare(a),
true,
)
users.sort(compareByFullNameReversed)
// [Drew, Charlie, Alice]
Cast a value into a mapping function → PR #43
Improve your own utility function by adding a flexible value-mapping option, using castMapping
to retrieve a mapping function.
The following types can be casted into a mapping function:
- Function: If the input is a function, it returns the function as is.
- Property Name: If the input is a property name, it returns a function that retrieves the value of that property from an object.
- Nullish: If the input is nullish (null or undefined), it returns a function that simply returns the input object itself.
import * as _ from 'radashi'
// Using a property name
const getName = _.castMapping('name')
getName({ name: 'Alice' }) // => 'Alice'
// Using a function
const getLength = _.castMapping((str: string) => str.length)
getLength('Hello') // => 5
// Using undefined
const identity = _.castMapping(undefined)
identity({ any: 'value' }) // => { any: 'value' }
Limit the range of a variable number → PR #106
The clamp
function restricts a number to be within a specified range.
Parameters:
value
is the number to clamp.min
is the minimum value (inclusive).max
is the maximum value (inclusive).
import * as _ from 'radashi'
_.clamp(5, 1, 10) // returns 5
_.clamp(0, 1, 10) // returns 1
_.clamp(15, 1, 10) // returns 10
// Invalid range
_.clamp(1, 10, 1) // throws
Add unzip
function → PR #64
The unzip
function creates an array of ungrouped elements, where each resulting array contains all elements at a specific index from the input arrays. It's the functional opposite of zip
.
import * as _ from 'radashi'
_.unzip([
['a', 1, true],
['b', 2, false],
])
// => [
// ['a', 'b'],
// [1, 2],
// [true, false],
// ]
Allow filtering of object keys → PR #28
Improve your own utility function by adding a flexible key-filtering option, using filterKey
to retrieve a filtering function. The returned function expects an object and a key, and returns true
if the key passes the filter.
The following types can be casted into a filtering function:
- Array: If the input is an array, it returns a function that checks if the key is included in the array.
- Function: If the input is a function, it returns the function as is.
- Nullish: If the input is nullish (null or undefined), it returns a function that always returns
true
.
import * as _ from 'radashi'
const obj = { a: 1, b: 2, c: 3 }
_.filterKey(obj, 'a', ['a', 'b']) // true
_.filterKey(obj, 'b', ['a', 'b']) // true
_.filterKey(obj, 'c', ['a', 'b']) // false
_.filterKey(obj, 'a', key => key < 'b') // true
_.filterKey(obj, 'b', key => key < 'b') // false
_.filterKey(obj, 'c', key => key < 'b') // false
_.filterKey(obj, 'a', null) // true
_.filterKey(obj, 'b', null) // true
_.filterKey(obj, 'c', null) // true
Check for a plain object → PR #16
The isPlainObject
function can be used to determine if a value is a plain object, as opposed to an instance of a custom class or a special object like Date
.
import * as _ from 'radashi'
_.isPlainObject({}) // => true
_.isPlainObject(Object.create(null)) // => true
_.isPlainObject([]) // => false
_.isPlainObject(null) // => false
_.isPlainObject(new Date()) // => false
Check for a Result
tuple → PR #172
New functions have been added to help work with "Result" tuples, which represent the success or failure of a tryit
call.
isResult
checks if a value is a Result tuple.isResultOk
checks if a Result tuple represents a successful operation.isResultErr
checks if a Result tuple represents a failed operation.
import * as _ from 'radashi'
if (_.isResult(value)) {
value // => [error?: unknown, result?: unknown]
}
if (_.isResultOk(value)) {
value[1] // <-- This is the resulting value!
}
if (_.isResultErr(value)) {
value[0] // <-- This is the error!
}
🔗 isResult Docs / Source / Tests
🔗 isResultOk Docs / Source / Tests
🔗 isResultErr Docs / Source / Tests
Check for an integer string → commit fa500d3
The isIntString
function returns true
if the input is a string that represents an integer.
import * as _ from 'radashi'
_.isIntString('12') // => true
_.isIntString('-12') // => true
_.isIntString('12.233') // => false
_.isIntString('12.0') // => false
_.isIntString('+12') // => false
_.isIntString('hello') // => false
_.isIntString(null) // => false
_.isIntString(12) // => false
Check for an error → PR #173
The isError
function returns true
for objects that inherit from Error
.
import * as _ from 'radashi'
_.isError(new Error()) // => true
_.isError(new TypeError()) // => true
_.isError('An error occurred') // => false
_.isError({ message: 'Error' }) // => false
Check for a boolean → commit adc419d
The isBoolean
function returns true
for boolean primitives. Boxed boolean values (e.g. new Boolean(false)
) are not considered booleans by this function.
import * as _ from 'radashi'
_.isBoolean(true) // => true
_.isBoolean(false) // => true
_.isBoolean(new Boolean(true)) // => false
_.isBoolean('true') // => false
_.isBoolean(1) // => false
_.isBoolean(undefined) // => false
Check for a RegExp object → PR #77
The isRegExp
function returns true
for RegExp
instances, even if they are subclass instances or from other realms.
import * as _ from 'radashi'
_.isRegExp(/.+/) // true
_.isRegExp(new RegExp('.+')) // true
_.isRegExp(new (class extends RegExp {})('.+')) // true
Check for a Set object → PR #77
The isSet
function returns true
for Set
instances, even if they are subclass instances or from other realms.
import * as _ from 'radashi'
_.isSet(new Set()) // true
_.isSet(new (class extends Set {})()) // true
Check for a Map object → PR #77
The isMap
function returns true
for Map
instances, even if they are subclass instances or from other realms.
import * as _ from 'radashi'
_.isMap(new Map()) // true
_.isMap(new (class extends Map {})()) // true
Check for a WeakMap object → PR #77
The isWeakMap
and isWeakSet
functions return true
for WeakMap
and WeakSet
instances, respectively, even if they are subclass instances or from other realms.
import * as _ from 'radashi'
_.isWeakMap(new WeakMap()) // true
_.isWeakMap(new (class extends WeakMap {})()) // true
_.isWeakSet(new WeakSet()) // true
_.isWeakSet(new (class extends WeakSet {})()) // true
🔗 isWeakMap Docs / Source / Tests
🔗 isWeakSet Docs / Source / Tests
Interpolate between two numbers → PR #86
The lerp
function is used to linearly interpolate between two numbers based on a specified ratio between 0 and 1. For example, a ratio of 0.5 would return the number exactly halfway between the two input numbers.
import * as _ from 'radashi'
_.lerp(0, 10, 0.5) // => 5
_.lerp(5, 15, 0.2) // => 7
_.lerp(-10, 10, 0.75) // => 5
noop and always → commit eb77c8f
The noop
function is a callback that does nothing and returns undefined
.
You don't call this directly. It's often useful in default parameters or destructuring to ensure a callback is defined.
import * as _ from 'radashi'
_.noop() // => undefined
_.noop(1) // => undefined
_.noop(1, 2, 3) // => undefined
The always
function creates a function that always returns the same value. This can be useful for providing a constant value as a callback. Note that the following example is overkill (for constant values, use an arrow function instead), but it demonstrates the concept:
import * as _ from 'radashi'
const alwaysTrue = _.always(true)
alwaysTrue() // true
alwaysTrue() // true
alwaysTrue() // true
🔗 always Docs / Source / Tests
Flip the arguments of a function → PR #35
The flip
function returns a new function that swaps the first two arguments of the original function.
This is most useful for reversing the order of a "comparator" (i.e. a function used for sorting), which effectively reverses the sort order.
import * as _ from 'radashi'
const subtract = (a: number, b: number) => a - b
subtract(1, 2) // => -1
_.flip(subtract)(1, 2) // => 1
New Features
Allow debounce
function to run immediately → PR #128
The debounce
function now accepts a leading
option. When true, the source function is called immediately on the first invocation of the debounced function, and subsequent calls will be debounced.
import * as _ from 'radashi'
const handleClick = _.debounce({ delay: 100, leading: true }, () => {
// This function will be called immediately on the first click,
// and then again after 100ms of no further clicks.
console.log('Clicked!')
})
handleClick() // Clicked!
handleClick() // (nothing)
handleClick() // (nothing)
// After 100ms: Clicked!
Add trigger
method to throttle
function → PR #135
The throttle
function now includes a trigger
method on the returned throttled function. This allows the wrapped function to be invoked immediately, bypassing any throttling that may be in effect. After the trigger
method is called, a new throttled call will be allowed after the specified interval has passed.
This can be useful when you want to ensure a function is called at least once, even if it's being throttled.
import * as _ from 'radashi'
const logMessage = (message: string) => {
console.log(`Message: ${message}`)
}
const throttledLog = _.throttle({ interval: 1000 }, logMessage)
throttledLog('First call') // Logs immediately
throttledLog('Throttled') // Doesn't log (throttled)
// Force a log, bypassing the throttle
throttledLog.trigger('Forced log') // Logs immediately
// Check if it's still throttled
throttledLog.isThrottled() // => true
Add trailing
option to throttle
function → PR #127
The throttle
function now accepts an optional trailing
option, which controls whether the wrapped function should be called after the throttle period if it was invoked during the throttled time.
This can be useful when you want to ensure the function is called at the end of a series of rapid invocations, even if it's being throttled.
import * as _ from 'radashi'
// Throttle a scroll event handler, calling it after each throttle period
const handleScroll = () => {
console.log('Scroll position:', window.scrollY)
}
const throttledScroll = _.throttle(
{ interval: 200, trailing: true },
handleScroll,
)
window.addEventListener('scroll', throttledScroll)
// Throttle an API call, calling it after each throttle period
const throttledFetch = _.throttle(
{ interval: 5000, trailing: true },
async () => {
const response = await fetch('https://api.example.com/data')
const data = await response.json()
console.log(data)
},
)
Provide index to mapify
callbacks → PR #100
The mapify
function now provides an index
argument to the getKey
and getValue
callbacks. This allows you to base the key or value on the current index of the array item, in addition to the item itself.
import * as _ from 'radashi'
const items = [
{ id: 1, name: 'Item 1' },
{ id: 2, name: 'Item 2' },
{ id: 3, name: 'Item 3' },
]
_.mapify(
items,
(item, index) => `item-${index}`,
item => item.name,
)
// Map {
// 'item-0' => 'Item 1',
// 'item-1' => 'Item 2',
// 'item-2' => 'Item 3',
// }
Add reversible castComparator
→ commit 1d7937e
The castComparator
function now accepts an optional reverse
argument that, when true
, will reverse the order of the comparison.
import * as _ from 'radashi'
const compareByName = _.castComparator('name')
const compareByNameReversed = _.castComparator('name', null, true)
const items = [
{ id: 1, name: 'Item 1' },
{ id: 2, name: 'Item 2' },
{ id: 3, name: 'Item 3' },
]
items.sort(compareByName)
// => [
// { id: 1, name: 'Item 1' },
// { id: 2, name: 'Item 2' },
// { id: 3, name: 'Item 3' },
// ]
items.sort(compareByNameReversed)
// => [
// { id: 3, name: 'Item 3' },
// { id: 2, name: 'Item 2' },
// { id: 1, name: 'Item 1' },
// ]
Allow pick
function to accept a callback for advanced picking → PR #30
The pick
function can also accept a predicate function as the filter argument. This allows for more complex filtering logic beyond simple key inclusion or exclusion.
import * as _ from 'radashi'
const source = { a: 1, b: 2, c: 3, d: 4 }
_.pick(source, (value, key) => {
return value % 2 === 0 // Include only even values
})
// => { b: 2, d: 4 }
Let select
function work without a condition callback → PR #9
The select
function now allows you to omit the condition
callback parameter. When the condition
parameter is not provided, any nullish values returned by your mapping function will be filtered out.
import * as _ from 'radashi'
const list = [{ a: 1 }, { b: 2 }, { a: 3 }, { a: null }, { a: undefined }]
const result = _.select(list, obj => obj.a)
// result = [1, 3]
Allow keys of any type in the unique
function → PR #10
The unique
function now supports any value as the key for identifying unique items in the array, not just strings, numbers, or symbols. The toKey
callback argument can return any kind of value.
import * as _ from 'radashi'
const list: any[] = [
null,
null,
true,
true,
'true',
false,
{ id: 'a', word: 'hello' },
{ id: 'a', word: 'hello' },
]
const result = _.unique(list, val => (val && val.id) ?? val)
// result = [null, true, 'true', false, { id: 'a', word: 'hello' }]
Allow keys of any type in the intersects
function → PR #11
The intersects
function now allows the identity
callback argument to return any value, instead of just a string, number, or symbol.
As a result, you can now omit the identity
callback for arrays of objects.
import * as _ from 'radashi'
const obj1 = { id: 1 }
const obj2 = { id: 2 }
const obj3 = { id: 3 }
const hasIntersection = _.intersects([obj1, obj2], [obj2, obj3])
// => true
const hasNoIntersection = _.intersects([obj1], [obj3])
// => false
Bug Fixes
Fix camel-cased to pascal-cased string conversion → PR #178
The pascal
function now correctly handles camel-cased strings, fixing issues with inputs like "helloWorld"
. It uses a regex to identify word boundaries and preserve existing capitalization.
import * as _ from 'radashi'
_.pascal('helloWorld') // => 'HelloWorld'
Use -1 as index for toKey()
with toggled item
→ PR #167
The toggle
function has been updated to pass an index of -1
when calling the toKey
function with the item
to be toggled. This ensures that the toKey
function can correctly identify the item, even if it doesn't exist in the array.
Previously, the toKey
function was called with the current index of the item in the array, which could lead to incorrect behavior if the item didn't exist.
import * as _ from 'radashi'
const gods = ['ra', 'zeus', 'loki']
_.toggle(gods, 'vishnu', (item, index) => {
console.log(`Index for '${item}': ${index}`)
return item
})
// Console output:
// Index for 'vishnu': -1
// Index for 'ra': 0
// Index for 'zeus': 1
// Index for 'loki': 2
// => ['ra', 'zeus', 'loki', 'vishnu']
Fix handling of period-containing property names → PR #95
The crush
function has been updated to correctly handle object properties with periods in their names. Previously, such properties would always have an undefined value in the resulting object.
import * as _ from 'radashi'
const data = {
name: 'ra',
'children.0.name': 'hathor',
}
_.crush(data) // => { name: 'ra', 'children.0.name': 'hathor' }
Fix null handling in assign
→ PR #112
The assign
function has been updated to correctly handle the case where a nested object is being overridden with a null
value. Previously, the function would incorrectly preserve the nested object, even when the override value was null
.
import * as _ from 'radashi'
const initial = { name: 'ra', children: { age: 42 } }
const override = { children: null }
_.assign(initial, override) // => { name: 'ra', children: null }
Handle falsy input as expected in toggle
→ PR #82
The toggle
function now handles falsy input more consistently. Previously, a falsy item
argument would be ignored, neither added nor removed from the array. Now, this only happens for undefined
values. As such, you should still avoid having undefined
in your array when using toggle
with it (since it still can't be removed by toggle
).
import * as _ from 'radashi'
_.toggle([], null) // [null]
_.toggle([null], null) // []
_.toggle([], false) // [false]
_.toggle([false], false) // []
_.toggle([], 0) // [0]
_.toggle([0], 0) // []
_.toggle([], '') // ['']
_.toggle([''], '') // []
_.toggle([], undefined) // []
toInt
and toFloat
should not throw on symbols → PR #67
The toInt
and toFloat
functions now handle symbols more gracefully. Instead of throwing an error, they will return NaN
if the input value is a symbol, and fall back to the provided default value if one is specified.
import * as _ from 'radashi'
_.toInt(Symbol('foo')) // NaN
_.toInt(Symbol('foo'), 0) // 0
_.toFloat(Symbol('bar')) // NaN
_.toFloat(Symbol('bar'), 0.0) // 0.0
Use typeof
in isFunction
→ commit 6ad96f4
The isFunction
utility now uses the typeof
operator instead of duck typing to determine if a value is a function. This ensures the type-guarded value can actually be called like a function, where before an object with call
and apply
properties would be falsely identified as a function.
import * as _ from 'radashi'
_.isFunction(() => {}) // => true
_.isFunction(MyClass) // => true
_.isFunction({ call: () => {}, apply: () => {} }) // => false
Avoid using isObject
internally and use isPlainObject
instead → PR #18
The internal use of isObject
has been replaced with isPlainObject
to ensure that objects created with Object.create(null)
are handled correctly. This change improves the consistency and robustness of the library.
import * as _ from 'radashi'
const object = Object.create(null)
object.a = 1
object.b = [2]
object.c = { d: 3 }
_.assign({}, object) // { a: 1, b: [2], c: { d: 3 } }
_.keys(object) // ['a', 'b.0', 'c.d']
Avoid false positive of array index in set
function → PR #15
The set
function has been improved to correctly handle keys that start with numbers. It now uses the isIntString
function to detect if a key should be treated as an array index.
import * as _ from 'radashi'
_.set({}, 'cards.2value', 2) // { cards: { "2value": 2 } }
Other Improvements
Performance
- assign
- Remove inefficiencies in loop. → PR #13
- cluster
- Avoid an array allocation by using a
for
loop instead ofArray.fill().map()
→ PR #63
- Avoid an array allocation by using a
- fork
- Avoid excessive array allocation by using a
for
loop and direct array updates instead ofreduce()
→ PR #33
- Avoid excessive array allocation by using a
- keys
- Avoid excessive array allocations. → PR #25
- merge
- Avoid calling the user-provided
key
generator more than once per item and avoid the use of an arrow function in the loop. → PR #60
- Avoid calling the user-provided
- replace
- Avoid creating 2 intermediate arrays. → PR #61
- replaceOrAppend
- Avoid creating 2 intermediate arrays. → PR #62
- series
- Avoid object spread in the loop by using a
for
loop and individual object property assignments instead → PR #37
- Avoid object spread in the loop by using a
- shuffle
- Use the Fisher-Yates algorithm. → PR #76
- template
- Use a
while
loop and string concatenation instead ofArray.from().reduce()
→ PR #32
- Use a
Refactoring
- retry
- Stop using
range()
unnecessarily, reducing the size ofretry
. → commit 5d60893
- Stop using
Added Types
- Added
MemoOptions<T>
type which represents the options object passed to thememo
function. → commit 877a1e4 - Added
TryitResult<T>
type which represents the return type of thetryit
function. → commit f044364 - Exported
UppercaseKeys
andLowercaseKeys
types from thelowerize
andupperize
functions respectively. → commit 96b28b9 - Add
FilteredKeys
type that extracts the keys of an object that pass a given filter. → commit 6a6f899 - Add the Ok/Err/Result/ResultPromise types. → PR #132
- Add
Assign
type to represent the return type of theassign
function. → PR #142
Type Fixes
- assign
- Make
assign
return type more accurate. → PR #142
- Make
- group
- Ensure
group
's return type is compatible withmapValues
's function type. → PR #24
- Ensure
- isArray
- Make
isArray
work withreadonly
array types. → commit 88c12b6 - Improve the
isArray
return type forunknown
input type. → PR #72 - Handle tuples in the
isArray
return type. → commit 9257535
- Make
- isPromise
- Align
isPromise
return type with its logic. → PR #175
- Align
- omit
- Allow the
keys
argument to be a readonly array. → PR #272
- Allow the
- select
- Make the
select
function more option-friendly by acceptingnull
orundefined
for thecondition
argument. → commit c9cfcd0
- Make the
- series
- Allow the
items
argument to be a readonly array → PR #14
- Allow the
- shake
- Stop using
Omit
on return type and givefilter
argument a safer type → PR #12
- Stop using
- shift
- Fix
shift
function to accept a readonly array type. → PR #126
- Fix
- sum
- Remove type constraint for mapped array passed to
sum
. → PR #143
- Remove type constraint for mapped array passed to
- zip
- Allow
zip
to accept readonly arrays. → commit f7d93cc
- Allow