
Security News
Axios Maintainer Confirms Social Engineering Attack Behind npm Compromise
Axios compromise traced to social engineering, showing how attacks on maintainers can bypass controls and expose the broader software supply chain.
matcha_match
Advanced tools
Pattern Matching for Typescript and Javascript
matcha provides powerful pattern matching - inspired by f# and functional programming.
npm i matcha_match
...
import { patternMatch, with_ } from 'matcha_match'
import { $string } from 'matcha/runtime-interfaces/$string
Pattern matching takes a value and matches it against a series of patterns. The first pattern to match, fires the value (with type inferred from the pattern) into an accompanying function.
So... let's say we have name.
We could do something like...
patternMatch(
name,
with_('garfield', matchedName => `${matchedName} is a cat`)
with_('odie', matchedName => `${matchedName} is a dog`)
)
In the above matchedName in both cases is inferred to be a string - even though name may be of unknown type.
That's because matchedName infers it's type from the pattern.
Pattern Matching can be used to return a value. The result is the result of the function that fires upon match. If there is no match, then the original value is returned instead.
const name: string = getName()
const a = patternMatch(
name,
with_('garfield', matchedName => `${matchedName} is a cat`)
with_('odie', matchedName => `${matchedName} is a dog`)
)
In the above, since the value and both with_ arms all return a string - the compiler is smart enough to know that the resulting type is always string. Therefore a gets an inferred type of string.
If one of the arms returned a number then a would have an inferred type of string | number.
We've already seen how simple equality matches can be made...
const a = 'cat' as unknown
const b = patternMatch(
a,
with_('cat', _ => `hello kitty`),
with_('dog', _ => `hello doggy`)
)
But Pattern Matching is far more powerful than that...
Objects and arrays can be matched against a partial object / array.
const a = {
name: {
first: 'johnny',
last: 'bravo'
}
}
patternMatch(
a,
with_({ name: { first: 'johnny '} }, _ => `matching on first name`)
)
Which is particularly useful when used in combination with destructuring
patternMatch(
a,
with_({ name: { first: 'johnny '} }, ({ name: { first: b }}) => `Hey it's ${b}`)
)
Special runtime interfaces can be used to match against in place of values...
Here we use $string in place of the literal 'johnny'.
const $matchPattern = {
name: {
first: $string
}
}
patternMatch(
a,
with_($matchedPattern, ({ name: { first: b }}) => `${b} is a string`)
)
It's also good to point out that a runtime interface automatically binds the correct type to the interface, so $string is of type string. So when a is matched, it infers the type { name: { first: string }}
Runtime interfaces are powerful...
const a = [1, 2, 3]
patternMatch(
a,
with_($array($number), a => `${a} is an array of numbers`)
)
patternMatch(
a,
with_([1, $number, 3], ([_, b, __]) => `${b} is a number`)
)
const a = {
a: [1, 2],
b: [3, 3, 4],
c: [1, 5, 99]
}
patternMatch(
a,
with_($record($array($number)), a => `A record of arrays of numbers - whoa`)
)
const a = 'cat' as unknown
console.log(
patternMatch(
a,
with_($lt(100), _ => `< 100`),
with_($gt(100), _ => `> 100`),
with_(100, _ => `its 100`),
with_($unknown, _ => `no idea ... probably a cat`) // Use $unknown as a catch all
)
)
const a = 'cat' as string | number
patternMatch(
a,
with_($union([$string, $number]), _ => `a is string | number`)
)
Runtime interfaces include
$string$number$boolean$array([])$record()$union([])$unknown$nothing <- Use this to match on undefined & null$lt$gt$lte$gte
const $even =
{
runtimeInterface: true,
test: (a: number) => a % 2 === 0
} as unknown as number
const $odd =
{
runtimeInterface: true,
test: (a: number) => a % 2 !== 0
} as unknown as number
console.log(
patternMatch(
101,
with_($even, _ => `number is even`),
with_($odd, _ => `number is odd`)
)
) // number is odd
A Runtime interface is an object with the property runtimeInterface: true.
This tells the with_ function to treat the value as a Runtime Interface.
Primitive Runtime Interfaces have a type property, but more complex ones have a test function that determines whether a match is being made.
In both $odd and $even the subject is piped into the test function and a boolean is returned which determines whether or not the subject matches.
Note that the Runtime Interface object is coerced into the expected type should the path match.
const $validJson = {
userId: $number,
id: $number,
title: $string,
completed: $boolean
}
fetch('https://jsonplaceholder.typicode.com/todos/1')
.then(response => response.json())
.then(json =>
patternMatch(
json,
match($validJson, json => console.log(`yay - ${ json.title }`)),
match($unknown, a => console.log(`Unexpected JSON response from API`))
)
)
Pattern matching becomes more powerful when used to drive type-cirtainty.
The return value of pattern matching is often a union type or just plain unknown.
Instead we can drive type-cirtainty by not returning a response to a variable at all. Instead we call a function passing in the value of cirtain-type from the inferred match.
In the below personProgram only fires if bob matches $person so if personProgram runs at all, then it is with type-cirtainty.
const $person = {
name: {
first: $string
}
}
type Person = typeof $person
const personProgram = (person: Person) => {
//this program runs with type cirtainty :D
console.log(`${person.name.first} is safe`)
}
const bob = getPerson(123)
patternMatch(
bob,
with_($person, personProgram /* this only runs if a match occurs */),
with_($nothing, _ => console.log('no match'))
)
FAQs
Pattern matching for Typescript and Javascript
The npm package matcha_match receives a total of 1 weekly downloads. As such, matcha_match popularity was classified as not popular.
We found that matcha_match demonstrated a not healthy version release cadence and project activity because the last version was released a year ago. It has 1 open source maintainer collaborating on the project.
Did you know?

Socket for GitHub automatically highlights issues in each pull request and monitors the health of all your open source dependencies. Discover the contents of your packages and block harmful activity before you install or update your dependencies.

Security News
Axios compromise traced to social engineering, showing how attacks on maintainers can bypass controls and expose the broader software supply chain.

Security News
Node.js has paused its bug bounty program after funding ended, removing payouts for vulnerability reports but keeping its security process unchanged.

Security News
The Axios compromise shows how time-dependent dependency resolution makes exposure harder to detect and contain.