match-iz
Version:
A tiny pattern-matching library in the style of the TC39 proposal
963 lines (601 loc) • 19.5 kB
Markdown
# Changelog
All notable changes to this project will be documented in this file.
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
## Unreleased
## [5.1.1] - 2026-07-21
### Fixed
- **Types packaging (5.1.0 regression):** `index.d.ts` no longer mixes top-level
`export type` with runtime API only inside `declare module 'match-iz'`.
Typings are a single ESM-style surface (`export function` / `export type` at
file scope) so ESLint `import/named` and similar resolvers see named value
exports (`when`, `pluck`, …) while type helpers remain importable. Runtime
unchanged.
## [5.1.0] - 2026-07-19
### Added
- Public type exports for pluck/rest pattern machinery (for downstream libraries
such as `sift-r`):
- `Plucked`, `ExtractPlucked`
- `Rest`, `ExtractRest`, `RestFromArray`, `RestFromObject`
- `PatternHandlerInput<P>` — first `when` handler argument for a single pattern
- `PatternHandlerInputFromAll<Ps>` — same for multi-pattern / multi-guard `when`
### Fixed
- Multi-guard `when(P, G1, …, handler)` overloads now honour `pluck()` on any
of the patterns (same semantics as single-pattern / curried `when`). The
handler’s first argument is the plucked payload when present; otherwise the
intersection of each pattern’s `PatternAsType` (previous behaviour).
- Completes the 5.0.7 WIP: pluck typing now covers **all** `when` overloads
(1 pattern, curried, and 1–5 guards).
## [5.0.8] - 2026-07-18
### Added
- `match-iz/asserts` — invariant-style pattern assertions:
- `asserts(pattern)(value, description?)` throws `TypeError` on mismatch
(bad value as `error.cause`) and narrows `value` to `PatternAsType<P>`
- `makeAsserter(onFailure?)` for custom failure handling (must throw for
narrowing to remain sound)
- `rest()` / `...rest()` types the **second** `when` handler argument:
- Array patterns: `rest(isString)` → `string[]` (or `unknown[]` when unfiltered)
- Object patterns: `...rest(isNumber)` → `Record<string, number>` (or
`Record<string, unknown>` when unfiltered)
- Applies to curried `when`, multi-guard overloads, and the primary pattern
- `pluck()` typing for the **first** handler argument is hardened (works with
curried `when` and no longer leaks internal brand keys via `PatternAsType`)
- `pnpm run test:types` — compile-time checks for rest/pluck inference
(`tests/types/`)
- `pnpm run test:coverage` — Node native coverage over `dist/**` and `dates/**`
- Broader date-helper runtime coverage (`tests/date-helpers.test.mjs`)
- README: typing notes for TypeScript / `checkJs` consumers
### Fixed
- Rest brand no longer pollutes the first handler argument (object-spread
`...rest()` was previously collapsed via structural typing)
- Pluck brands stripped from `PatternAsType` the same way as rest brands
- `HandlerValue` uses a non-distributive `[ExtractPlucked<P>] extends [never]`
check
- Local `jsconfig.json` no longer path-maps `match-iz` to untyped `src/`
(broke IDE inference when testing in this repo)
- Package `exports` condition order: `types` before `import`/`require`
(correct TypeScript resolution; silences esbuild warnings)
### Chore
- Dev-dependency upgrades: esbuild 0.28, eslint 10, typescript-eslint 8.64,
typescript 5.9, nodemon 3, rimraf 6, globals 17, mitata, ts-pattern, dedent,
`@eslint/js`
- ESLint flat config uses native `@eslint/js` + `typescript-eslint` (no
`@eslint/eslintrc` FlatCompat)
- TypeScript held at **5.9.x** (`typescript-eslint` still requires
`typescript < 6.1`)
## [5.0.7] - 2025-07-18
- Using `pluck()` now affects the type of the handler-argument. This is a
WIP, it does not apply to all `when`-overloads.
- Update types for `isStrictly`, `includedIn`, `falsy`, `truthy`, `includes`,
`not`, `spread`, `defined`, `empty`
## [5.0.6] - 2025-07-17
- Fill-out some more JSDoc into the typedef files.
## [5.0.3] - 2025-07-04
- Added JSDoc descriptions/examples back into `index.d.ts`
## [5.0.2] - 2025-07-02
- Improved `index.d.ts` to give typings to `when()` callbacks and return-types
for `match()` and `against()`. Not perfect, but better.
## [5.0.1] - 2025-01-14
### Added
- Very basic benchmarks. Use `pnpm run bench`
### Updated
- README includes info about `rest`
## [5.0.0] - 2024-12-31
### BREAKING
- Exact array matches now require `eq`:
```javascript
//
// <= match-iz@4 behaviour
//
match([1, 2, 3])(
when([1, 2], 1),
when([1, 2, 3], 2),
otherwise(3),
)
// 2 (array-matching was exact by default)
//
// v5 behaviour onwards:
//
match([1, 2, 3])(
when([1, 2], 1),
when([1, 2, 3], 2),
otherwise(3),
)
// 1 (because array-matching is now partial)
match([1, 2, 3])(
when(eq([1, 2]), 1),
when(eq([1, 2, 3]), 2),
otherwise(3),
)
// 2 (use `eq` to make array-matching exact)
```
Notice with v5 that array matching is now partial, hence the first example
returning `1` instead of `2` as in `<=v4`.
### Added
- `rest` introduced for slurping remaining values of arrays or objects:
```javascript
// Objects
match({ one: 1, two: 2, three: 3 })(
when({ one: 1, ...rest(isNumber) }, (_, rest) => {
console.log(rest);
// { two: 2, three: 3 }
}),
)
// Arrays
match([1, 2, 3])(
when([1, rest(isNumber)], (_, rest) => {
console.log(rest);
// [2, 3]
}),
)
```
Notice `rest()` is used for arrays, and `...rest()` for objects.
You can omit the argument to `rest` to match/capture everything:
```javascript
// Objects
match({ one: 1, two: 2, three: 'three' })(
when({ one: 1, ...rest() }, (_, rest) => {
console.log(rest);
// { two: 2, three: 'three' }
}),
)
// Arrays
match([1, 2, 'three'])(
when([1, rest()], (_, rest) => {
console.log(rest);
// [2, 'three']
}),
)
```
Caveat: `rest` cannot yet be nested in sub-arrays or sub-objects.
### Updated
- `eq` now works with arrays as well as objects. Change made in order to
support v5 `rest` helper, since that will require a breaking-change for which
the updated `eq` is the fallback for.
```javascript
match([1, 2, 3])(
when(eq([1, 2]), 1),
when(eq([1, 2, 3]), 2),
otherwise(3),
)
// 2
```
## [4.0.4] - 2024-04-14
### Fixed
- Adds types export to pkg.json#exports. Thanks to @olmesm.
- Fix lint command
## [4.0.3] - 2024-01-23
### Updated
- `index.d.ts`: Updated to parse patterns into types when the input is sent
into your `when` handlers:
Some examples:
```typescript
when({ one: 1 as const, two: isNumber }, (res) => {
// res will be typed as = { one: 1, two: number }
})
```
```javascript
when({ name: isString, age: isNumber }, (res) => {
// res will be typed as = { name: string, age: number }
})
```
You can have your own predicates conform by using the `is` keyword:
```typescript
function isString(value: any): value is string {
return typeof value === 'string';
}
```
## [4.0.2] - 2023-11-16
### Updated
- `index.d.ts`: Pass `Input` into `TPattern` so we can get autocomplete in `when()`s.
## [4.0.1] - 2023-06-19
### Fixed
- 'months' was broken for `inTheNext/Future/Past` because the minutes RegExp got there first, making `inTheNext(6, 'months')` assume 6 minutes.
## [4.0.0] - 2023-06-03
### BREAKING CHANGE
- `matchiz` now includes a runtime exhaustiveness check. If no patterns match and no `otherwise` was specified, an error will be thrown. `4.0.0` is otherwise identical to `3.10.0`.
## [3.10.0] - 2023-03-11
### Added
- New date pattern-helpers: `inDay/s`, `inMonth/s`, `inYear/s`
Usage:
```js
let date = new Date(2021, 0, 1, 12, 0, 0)
match(date)(
when(inDay(new Date(2021, 0, 1)), 'January 1st 2021'),
when(inDay(new Date(2021, 0, 2)), 'January 2nd 2021')
)
```
Use a 1-based month index by passing an array:
```js
let date = new Date(2021, 0, 1, 12, 0, 0)
match(date)(
when(inDay([2021, 1, 1]), 'January 1st 2021'),
when(inDay([2021, 1, 2]), 'January 2nd 2021')
)
```
The plural helpers accept arrays of date-objects and/or 1-based month-index arrays:
```js
let date = new Date(2021, 0, 1, 12, 0, 0)
match(date)(
when(inDays([[2021, 1, 1], new Date(2021, 0, 2)]), 'January 1st or 2nd 2021')
)
```
## [3.9.4] - 2022-10-14
### Added
- Test for browser build exports
### Fixed
- Fix browser build
## [3.9.3] - 2022-10-12
### Fixed
- Fix browser build
## [3.9.2] - 2022-10-12
### Added
- Typedefs + JSDoc for `getIterationLimit` and `setIterationLimit`
## [3.9.1] - 2022-10-12
### Fixed
- Fix browser build. The following now exist on the `matchiz` object: `firstOf`, `lastOf`, `some`, `every`, `isStrictly`.
## [3.9.0] - 2022-10-12
### Added
- Now matches on `arguments` keyword
- Now matches `Map`, `Set`, and iterators. See [PR for more details](https://github.com/shuckster/match-iz/pull/1).
## [3.8.1] - 2022-08-27
### Fixed
- Fix browser build
## [3.8.0] - 2022-06-23
### Added
- `eq` + `deepEq` for object equality comparison:
```js
match({ one: '1', two: '2', three: '3' })(
when({ one: '1', two: '2' }, 'partial match')
)
// 'partial match'
match({ one: '1', two: '2', three: '3' })(
when(eq({ one: '1', two: '2' }), 'shallow match')
)
// undefined
match({ one: '1', two: '2', three: { four: '4', five: '5' } })(
when(
eq({ one: '1', two: '2', three: eq({ four: '4' }) }),
'deep match using nested eq()'
)
)
// undefined
match({ one: '1', two: '2', three: { four: '4', five: '5' } })(
when(
deepEq({ one: '1', two: '2', three: { four: '4' } }),
'deep match using deepEq()'
)
)
// undefined
```
## [3.7.1] - 2022-06-01
### Added
- `isStrictly` typedef
## [3.7.0] - 2022-06-01
### Added
- `isStrictly` exact-match pattern-helper
## [3.6.0] - 2022-05-31
### Updated
- Implement basic generics into the typedefs
### Fixed
- Remove deprecated form of `anyOf()` from JSDoc
- Use uncurried form of `when()` in JSDoc
## [3.5.4] - 2022-05-03
### Fixed
- `when()` typedef overloads were not working properly
## [3.5.3] - 2022-04-26
### Updated
- Simplify README, link to [Wiki](https://github.com/shuckster/match-iz/wiki) for further docs
## [3.5.0] - 2022-04-23
### Added
- `isBefore()` / `isAfter()` date matchers:
```js
match(new Date())(
when(isBefore([2001, 1, 1]))('Before midnight January 1st, 2001'),
when(isAfter([1970, 5]))('After May 1970 (ie; June onwards)'),
when(isBefore([1999]))('Before 1999 (ie; up to and including 1998)'),
when(isAfter(new Date(2001, 0, 1)))('After midnight January 1st, 2001')
)
```
## [3.4.2] - 2022-04-21
### Fixed
- Add missing typedefs
## [3.4.1] - 2022-04-20
### Fixed
- Add missing exports to the browser-build
## [3.4.0] - 2022-04-19
### Added
- `every()` / `some()` for matching patterns in array haystacks:
```js
match(array)(
when(every(isNumber))('all items are numbers'),
when(some(isNumber))('some items are numbers'),
when(every({ id: isNumber }))('all items have an id property')
)
```
## [3.3.1] - 2022-04-15
### Removed / Fixed
- Remove `browser` setting from `package.json`; seems to cause issues with CodeSandbox build process, and isn't really necessary since the browser-build is something that's interacted with manually.
## [3.3.0] - 2022-04-14
### Added
- `inTheFuture` alias for `inTheNext`
- `inTheFuture` + `inThePast` can be used without arguments now
## [3.2.2] - 2022-04-14
### Updated
- README tweaks, typos
## [3.2.0] - 2022-04-14
### Added
- `firstOf()` and `lastOf()` matchers:
```js
match([1, 'a', 3, 4, 5, 6])(
when(lastOf(isNumber, isString), () => {
return 'last two items are a number and a string'
}),
when(firstOf(isNumber, isString), () => {
return 'first two items are a number and a string'
})
)
```
- Support `when` guards, which are essentially just consecutive patterns:
```js
match(haystack)(
when(isNumber, gt(5), lte(10), num => {
return 'a number between 6 and 10'
}),
when(isString, startsWith('a'), str => {
return 'a string starting with "a"'
}),
when(isArray, lastOf(isNumber, isNumber), arr => {
return 'the last two elements of the array are numbers'
})
)
```
## [3.1.0] - 2022-04-12
### Added
- `isTime` for testing milliseconds since Unix epoch
- New `match-iz/dates` methods for testing time-frames relative to now:
```js
match(date)(
// Test if a date is in the past
when(inThePast(1, 'day'))(...),
when(inThePast(2, 'days'))(...),
// Test if a date is in the future
when(inTheNext(30, 'minutes'))(...),
)
```
### Removed
- Old `isDate`/`isTime` typedefs were left behind
## [3.0.0] - 2022-04-09
### Removed
- Remove `isDate`/`isTime`. `isDate` conflicted with the same function in `types`, and could not have matchers passed into it in the same way as the individual helpers. ie; `isDate(inRange(), anyOf(), ...)` was not possible.
- Removed deprecated `when` OR syntax that abuses arrays. Use `anyOf` matcher instead.
```js
// Deprecated syntax:
when([allOf(isFri, isHour(gte(17)), isSat, isSun)])
// ^__ _ _ _ _ __^
// Refactored using `anyOf`:
when(anyOf(allOf(isFri, isHour(gte(17))), isSat, isSun))
// ^^^^^^__ _ _ _ _ __^
```
### Updated
- `when` now accepts a result/handler as its second argument:
```js
// New syntax:
when(literalOrPattern, resultOrHandler)
match(new Date())(
when(allOf(nthSun(-1), isMar), dateObj => {
return 'Last Sunday of March: Clocks go forward'
}),
when(anyOf(allOf(isFri, isHour(gte(17))), isSat, isSun), () => {
return 'Ladies and Gentlemen; The Weekend'
}),
otherwise('The clock is ticking')
)
// Existing syntax (still works):
when(literalOrPattern)(resultOrHandler)
match(new Date())(
when(allOf(nthSun(-1), isMar))(dateObj => {
return 'Last Sunday of March: Clocks go forward'
}),
when(anyOf(allOf(isFri, isHour(gte(17))), isSat, isSun))(() => {
return 'Ladies and Gentlemen; The Weekend'
}),
otherwise('The clock is ticking')
)
```
## [2.3.2] - 2022-04-07
### Fixed
- README typos, fixes
## [2.3.0] - 2022-04-07
### Added
- Simple date matchers now available in `match-iz/dates` and `match-iz/dates/utc`
## [2.2.0] - 2022-03-21
### Updated
- Permit use of anyOf() as standalone function instead of just match-iz pattern, eg:
```js
// Before 2.2.0
const isStringOrRegExp = anyOf(isString, isRegExp)
// [isString, isRegExp]
// After 2.2.0
const isStringOrRegExp = anyOf(isString, isRegExp)
// <Function>
isStringOrRegExp('foo') // true
isStringOrRegExp(/foo/) // true
```
## [2.1.0] - 2022-03-06
### Updated
- inRange() will work with min/max swapped
## [2.0.7] - 2022-02-10
### Fixed
- README typo
## [2.0.6] - 2022-02-10
### Updated
- README cleanup
## [2.0.5] - 2022-02-08
### Updated
- README tweaks, update deps
## [2.0.4] - 2022-02-06
### Fixed
- Include package.json for sub-modules.
## [2.0.3] - 2022-02-06
### Fixed
- Another run at fixing import vs. require.
## [2.0.2] - 2022-02-03
### Fixed
- Fix .d.ts for allOf, anyOf, includedIn, hasOwn
## [2.0.1] - 2022-02-03
### Fixed
- Added isDate() to .d.ts
## [2.0.0] - 2022-02-01
### Breaking Changes
- If specified, named RegExp-groups will be passed-in to `when` handlers as the first argument, and the full-match the second.
Before v2:
```js
when(/(?<foo>.*)/)(fullMatch => {
const foo = fullMatch.groups.foo
console.log(foo)
})
```
After v2:
```js
when(/(?<foo>.*)/)(({ foo }, fullMatch) => {
console.log(foo)
})
```
- RegExps without named-groups work as before, with the full-match being the first and only argument to the handler.
## [1.12.0] - 2022-01-12
### Added
- cata() helper for integration with ADTs/monads
## [1.11.2] - 2022-01-11
### Fixed
- Fix esbuild errors by re-ordering package.json exports
## [1.11.1] - 2022-01-10
### Updated
- esbuild output was different after deps-update for the browser-build
## [1.11.0] - 2021-12-28
### Added
- isDate
## [1.10.4] - 2021-12-21
### Fixed
- Fix WebPack error: "Default condition should be last one"
## [1.10.3] - 2021-11-26
### Updated
- Above-the-fold README tweaks
## [1.10.2] - 2021-10-29
### Fixed
- Whoops, README example condition for >=500 will not run
## [1.10.1] - 2021-10-29
### Updated
- README example tweaks
## [1.10.0] - 2021-10-24
### Added
- pluck(predicate?) helper to extract a part of the haystack
- instanceOf(constructor) helper
## [1.9.0] - 2021-10-23
### Added
- hasOwn(prop1, prop2...) helper
## [1.8.2] - 2021-10-18
### Fixed
- isNumber(NaN) should return false
## [1.8.1] - 2021-10-02
### Updated
- Tweak README
- Mild code shrinking
## [1.8.0] - 2021-09-20
### Added
- allOf(), anyOf(), includedIn()
## [1.7.3] - 2021-09-14
### Updated
- Minor cleanup
## [1.7.2] - 2021-09-11
### Updated
- `not()` type definition
## [1.7.1] - 2021-09-10
### Updated
- `not()` can be used with literals as well as functions
## [1.7.0] - 2021-09-10
### Added
- `not()` will negate the output of a function
## [1.6.4] - 2021-08-18
### Updated
- README: Compact full examples into details/summary tags
## [1.6.3] - 2021-08-10
### Fixed
- We're exposing CJS as well as ESM, so use .mjs instead of "type": "module" for build/source files. Fixes CJS imports
## [1.6.2] - 2021-08-10
### Fixed
- Use ESM for source/build files
- Fix ESM build (import { ... } from 'match-iz' was not working right)
## [1.6.1] - 2021-08-09
### Updated
- Will do the right thing with `NaN`
- Can handle sub-arrays as well as sub-pojos
## [1.6.0] - 2021-08-09
### Updated
- When matching array-to-array, ensure exact lengths before comparing contents to make matches more predictable. Comparing variable length arrays is done better via a custom when-predicate
## [1.5.0] - 2021-08-09
### Added
- Can now match against contents of an array
## [1.4.1] - 2021-08-08
### Added
- Add runtime type checkers to index.d.ts
## [1.4.0] - 2021-08-07
### Updated
- Export runtime type checkers, why not
## [1.3.6] - 2021-07-24
### Updated
- NPM tags, README tweaks, no functional changes
## [1.3.5] - 2021-07-24
### Fixed
- Remove @params / @returns from JSDoc comments - fixes duplicate Intellisense suggestions
## [1.3.4] - 2021-07-24
### Added
- `index.d.ts` against() example
## [1.3.3] - 2021-07-23
### Added
- Basic `index.d.ts` for type hints and examples
## [1.3.2] - 2021-07-22
### Fixed
- otherwise() was returning all falsy values as `undefined`
## [1.3.1] - 2021-07-21
### Updated
- Updated test for nested patterns
- Remove superfluous coercion
## [1.3.0] - 2021-07-21
### Added
- Support nested objects for patterns
## [1.2.1] - 2021-07-20
### Updated
- unpkg example in README
## [1.2.0] - 2021-07-19
### Removed
- Literals already cover use-case for isTrue/isFalse
## [1.1.2] - 2021-07-19
### Updated
- README: Above the fold examples
## [1.1.1] - 2021-07-18
### Fixed
- inRange() should check that value isNumber, not min/max
## [1.1.0] - 2021-07-18
### Fixed
- empty() should not include 'false'
### Added
- Provide truthy/falsy/isTrue/isFalse
## [1.0.4] - 2021-07-18
### Fixed
- Update README to include correct empty/defined usage
## [1.0.3] - 2021-07-18
### Fixed
- defined, empty, spead()
- More complete empty()
- Number.isNumber() is not a thing
## [1.0.2] - 2021-07-18
### Fixed
- RegExps guard for strings
- spread() should not mutate its argument
## [1.0.1] - 2021-07-18
### Fixed
- Browser-build global now `matchiz`, not `matches`
## [1.0.0] - 2021-07-18
### Added
- match-iz :)