Skip to content

v12.2.0 – Radashi's first stable release

Compare
Choose a tag to compare
@aleclarson aleclarson released this 01 Nov 21:25
· 81 commits to main since this release

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, and Set 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))

🔗 Docs / Source / Tests

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

🔗 Docs / Source / Tests

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

🔗 Docs / Source / Tests

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']

🔗 Docs / Source / Tests

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 }

🔗 Docs / Source / Tests

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 or Math.ceil) can be provided. The default rounding function is Math.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

🔗 Docs / Source / Tests

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

🔗 Docs / Source / Tests

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

🔗 Docs / Source / Tests

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. for localeCompare use cases).
  • reverse reverses the comparison order if set to true.
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]

🔗 Docs / Source / Tests

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:

  1. Function: If the input is a function, it returns the function as is.
  2. Property Name: If the input is a property name, it returns a function that retrieves the value of that property from an object.
  3. 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' }

🔗 Docs / Source / Tests

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

🔗 Docs / Source / Tests

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],
// ]

🔗 Docs / Source / Tests

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:

  1. Array: If the input is an array, it returns a function that checks if the key is included in the array.
  2. Function: If the input is a function, it returns the function as is.
  3. 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

🔗 Docs / Source / Tests

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

🔗 Docs / Source / Tests

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

🔗 Docs / Source / Tests

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

🔗 Docs / Source / Tests

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

🔗 Docs / Source / Tests

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

🔗 Docs / Source / Tests

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

🔗 Docs / Source / Tests

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

🔗 Docs / Source / Tests

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

🔗 Docs / Source / Tests

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

🔗 noop Docs / Source / Tests

🔗 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

🔗 Docs / Source / Tests

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
  • cluster
    • Avoid an array allocation by using a for loop instead of Array.fill().map() → PR #63
  • fork
    • Avoid excessive array allocation by using a for loop and direct array updates instead of reduce() → PR #33
  • keys
  • 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
  • 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
  • shuffle
  • template
    • Use a while loop and string concatenation instead of Array.from().reduce() → PR #32

Refactoring

Added Types

  • Added MemoOptions<T> type which represents the options object passed to the memo function. → commit 877a1e4
  • Added TryitResult<T> type which represents the return type of the tryit function. → commit f044364
  • Exported UppercaseKeys and LowercaseKeys types from the lowerize and upperize 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 the assign function. → PR #142

Type Fixes

  • assign
  • group
    • Ensure group's return type is compatible with mapValues's function type. → PR #24
  • isArray
  • isPromise
    • Align isPromise return type with its logic. → PR #175
  • omit
    • Allow the keys argument to be a readonly array. → PR #272
  • select
    • Make the select function more option-friendly by accepting null or undefined for the condition argument. → commit c9cfcd0
  • series
    • Allow the items argument to be a readonly array → PR #14
  • shake
    • Stop using Omit on return type and give filter argument a safer type → PR #12
  • shift
    • Fix shift function to accept a readonly array type. → PR #126
  • sum
    • Remove type constraint for mapped array passed to sum. → PR #143
  • zip