Security News
Input Validation Vulnerabilities Dominate MITRE's 2024 CWE Top 25 List
MITRE's 2024 CWE Top 25 highlights critical software vulnerabilities like XSS, SQL Injection, and CSRF, reflecting shifts due to a refined ranking methodology.
@rorystokes/ad-ts
Advanced tools
Bringing Pattern Matching and other ADT and Functional Programming concepts to TypeScript. Borrows
heavily from and builds on top of @gcanti's fp-ts
library.
match
The match
submodule provides simple, type-safe 'pattern matching' across a
TaggedUnion
. This requires each instance of any of
the types in the union to have an explicit _tag
value to uniquely identify the type they belong
to.
Many of the types provided by fp-ts
including Option and Either
already have such tags, and as such a TaggedUnion
can be constructed using these types.
TaggedUnions
must be used instead of native TypeScript Unions as they encode the mapping from tags
to types in a form that the type system can leverage to provide safety in match methods.
caseclass
The caseclass
submodule is designed to supplement the usage of match
, providing an easy way to
define a tagged type, including specifying default values.
patternmatch
The patternmatch
submodule allows for more generic pattern matching across any values. It always
returns a fp-ts
Option, as exhaustivity testing is not
possible.
Patterns can either be simple boolean guards, or extractor methods that return optional extracted results.
do
The do
submodule aims to provide equivalent syntax to for
comprehensions in Scala or do
in
Haskell (see Wikipedia's page on Monads)
This depends heavily on the Monad
HKT defined by fp-ts
.
import { doOn } from '@rorystokes/ad-ts/do';
import { Either, either, left, right } from 'fp-ts/lib/Either';
const numberOrError = (s: string): Either<string, number> => {
const n = Number.parseFloat(s)
return Number.isNaN(n) ? left(`'${s}' is not a number`) : right(n)
}
const sqrtOrError = (n: number): Either<string, number> => {
const sqrt = Math.sqrt(n)
return Number.isInteger(sqrt) ? right(sqrt) : left(`${n} is not a square number]`)
}
const testString = (s: string) => doOn(either)
.with("s", s)
.bind("n", ({s}) => numberOrError(s))
.bind("sqrt", ({n}) => sqrtOrError(n))
.yield(({sqrt}) => sqrt)
testString("9") // right(3)
testString("8") // left("8 is not a square number")
testString("seven") // left("'seven' is not a number")
import { match, TaggedUnion, TypeTag } from '@rorystokes/ad-ts/match'
class Foo {
readonly [TypeTag]: "Foo"
constructor(readonly name: string) { }
}
class Bar {
readonly [TypeTag]: "Bar"
constructor(readonly fn: string, readonly sn: string) { }
}
type FooBar = TaggedUnion<{
Foo: Foo,
Bar: Bar
}>
const getName = (foobar: FooBar) => match(foobar)({
Foo: ({ name }) => name,
Bar: ({ fn, sn }) => `${fn} ${sn}`
})
getName(new Foo("Alan")) // "Alan"
getName(new Bar("Bob", "Brown")) // "Bob Brown"
import { match, TaggedUnion } from '@rorystokes/ad-ts/match'
import { CaseClass, Default } from '@rorystokes/ad-ts/caseclass'
const Foo = CaseClass("Foo")<{
name: string,
size?: number
}>()
type Foo = ReturnType<typeof Foo>
const Bar = CaseClass("Bar")<{
label: string,
prefix: Default<string>
}>({
prefix: "Bar:"
})
type Bar = ReturnType<typeof Bar>
type FooBar = TaggedUnion<{
Foo: Foo,
Bar: Bar
}>
const getName = (foobar: FooBar) => match(foobar)({
Foo: ({ name }) => name,
Bar: ({ label, prefix }) => `${prefix} ${label}`
})
getName(Foo({ name: "Alan" })) // "Alan"
getName(Bar({ label: "Bob" })) // "Bar: Bob"
getName(Bar({ label: "Carol", prefix: ">>" })) // ">> Carol"
import { patternmatch, Pattern } from '@rorystokes/ad-ts/patternmatch'
import { none, some } from 'fp-ts/lib/Option';
const IsInteger: Pattern<number, number> = Number.isInteger
const Square: Pattern<number, { i: number, sqrt: number }> = (i: number) => {
const sqrt = Math.sqrt(i)
return Number.isInteger(sqrt) ? some({ i, sqrt }) : none
}
const checkNumber = patternmatch<number>({ Square, IsInteger })({
Square: ({ sqrt, i }) => `${i} is ${sqrt}^2`,
IsInteger: (i) => `${i} is an integer`
})
checkNumber(3) // some("3 is an integer")
checkNumber(4) // some("4 is 2^2")
checkNumber(5.6) // none
See https://github.com/gcanti/fp-ts
import { match, TaggedUnion } from '@rorystokes/ad-ts/match'
import { Some, None, some, none } from 'fp-ts/lib/Option'
type TaggedOption<A> = TaggedUnion<{
Some: Some<A>,
None: None<A>
}>
const valueOrEmpty = (opt: TaggedOption<string>) => match(opt)({
Some: ({value}) => value,
None: () => "Empty"
})
valueOrEmpty(some("Full")) // "Full"
valueOrEmpty(none) // "Empty"
import { match, TaggedUnion } from '@rorystokes/ad-ts/match'
import { Left, Right, left, right } from 'fp-ts/lib/Either'
type TaggedEither<A,B> = TaggedUnion<{
Left: Left<A, B>,
Right: Right<A, B>
}>
const getString = (either: TaggedEither<number, string>) => match(either)({
Left: ({value}) => `#${value}`,
Right: ({value}) => `'${value}'`
})
getString(left(42)) // "#42"
getString(right("hello")) // "'hello'"
FAQs
ADTs for TypeScript
We found that @rorystokes/ad-ts 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
MITRE's 2024 CWE Top 25 highlights critical software vulnerabilities like XSS, SQL Injection, and CSRF, reflecting shifts due to a refined ranking methodology.
Security News
In this segment of the Risky Business podcast, Feross Aboukhadijeh and Patrick Gray discuss the challenges of tracking malware discovered in open source softare.
Research
Security News
A threat actor's playbook for exploiting the npm ecosystem was exposed on the dark web, detailing how to build a blockchain-powered botnet.