DEV Community

loading...

New RxJS Primitives release, new operators + Typescript 4

tanepiper profile image Tane Piper Originally published at tane.dev ・4 min read

This week I released new versions of my RxJS libraries in rxjs-primitives.
Since it's released I've added a few new utility operators. Some of these have been out for a while since I originally wrote about the release, so I've highlighted them here as they may be useful to some developers.

You can check out the full docs here.

Typescript 4

Upgrading to Typescript 4 has allowed the removal of polymorphic functions in place of
Vardic Tuple Types and is why there is a major bump on all packages.

This can be seen in the old and new
concat operator in the rxjs-string package.

Most of the tests have also been converted to rxjs-marbles allowing for more robust
Observable testing (if you are working with RxJS I highly recommend checking it out, it integrates well with runners like Jest).

it(
    'should return string value of string ending with passed character',
    marbles((m) => {
      const input = m.hot('-a-b-c-|', { a: 'test', b: 'testing', c: 'gone' });
      const subs = '^------!';
      const expected = m.cold('---y---|', { y: 'testing' });
      m.expect(input.pipe(filterEndsWith('g'))).toBeObservable(expected);
      m.expect(input).toHaveSubscriptions(subs);
    }),
  );
Enter fullscreen mode Exit fullscreen mode

rxjs-array

npm install @tinynodes/rxjs-array

In the array module there are some operators to use with finding
the difference or intersection between a source and a passed array, for example:

of(['a', 'b', 'd'])
 .pipe(difference(['a', 'c']))
 .subscribe(console.log) // ['b', 'd']

of(['a', 'b', 'd'])
 .pipe(intersects(['a', 'c']))
 .subscribe(console.log) // ['a']
Enter fullscreen mode Exit fullscreen mode

These methods accept an array, or an Observable<Array> of items to compare against.

The module also included a binarySearch operator which returns
a custom BinarySearchResult tuple.

rxjs-boolean

npm install @tinynodes/rxjs-boolean

A new Luhn algorithm operator luhnCheck is provided that does validation on
numbers such as credit cards, ID cards and other value schemes that use the check.

fromString('4485275742308327')
    .pipe(luhnCheck())
    .subscribe(console.log) // true, this is a valid credit card
Enter fullscreen mode Exit fullscreen mode

rxjs-number

npm install @tinynodes/rxjs-number

inRange / outOfRange and filterInRange / filterOutOfRange both all two numbers, the filter methods return the value from the source observable within the range of those values,
while the other methods return a boolean value if in range. An optional third value will include/exclude the range value
based on the method

fromNumber([-1, 0, 1, 2, 10, 11])
 .pipe(filterInRange(0, 10))
 .subscribe(console.log) // [0, 1, 2, 10]

// Passing true as the third parameter, the range numbers will also be excluded
fromNumber([-1, 0, 1, 2, 10, 11])
 .pipe(filterInRange(0, 10, true))
 .subscribe(console.log) // [1, 2]
Enter fullscreen mode Exit fullscreen mode

rxjs-string

npm install @tinynodes/rxjs-string

New operators such as titleize, repeat and
match add new utility features for strings. Where they can they also support localisation:

fromString('Mary had a little lamb')
 .pipe(titleize())
 .subscribe(console.log) // 'Mary Had A Little Lamb'

fromString('Mary had ä little lamb')
 .pipe(titleize('de-DE'))
 .subscribe(console.log) // 'Mary Had Ä Little Lamb'
Enter fullscreen mode Exit fullscreen mode

rxjs-utility

npm install @tinynodes/rxjs-utility

The utility module contains some specialised tap operators such as tapIf, startWithTap and tapOnSubscribe.
These provide a way to do side effects. With startWithTap it can be used with Angular to do a form touch, also tapOnSubscribe will
fire when there is a subscription to the Observable:

// Only touch on first value change
form.valueChange.pipe(
 startWithTap(() => this.onTouch())
).subscribe()

// Fire when a component subscribes to the service bus
this.serviceBus.pipe(
  tapOnSubscribe((name: string) => console.log(`New Subscription to ${name}`))
).subscribe()
Enter fullscreen mode Exit fullscreen mode

The tapIf will only fire if a passed method result is truthy:

fromNumber([1, 2, 3, 4, 5, 6]).pipe(
  tapIf((val) => val % 2 === 0), (val) => console.log(val)
).subscribe() // 2, 4, 6
Enter fullscreen mode Exit fullscreen mode

The last operator is mapIfSource which might be a bit of a weird one but I hope might become useful.

The operator takes the value from the source and passes to a predicate method, and depending on the result will map the
result of a passed method. A simple example would be:

fromNumber([1, 2, 3, 4, 5, 6]).pipe(
  mapIfSource(
    (value) => val % 2 === 0,
    (value) => val * 10,
    (value) => val * 20
  )
).subscribe() // 20, 20, 60 40, 100, 60
Enter fullscreen mode Exit fullscreen mode

Here, if the result of the predicate is true multiply by 10, otherwise by 20. The method is typed to allow different return values
based on the result (so you will have to handle the type later). For example we could even turn it into a
FizzBuzz operator:

export function fizzbuzz(): OperatorFunction<number, string | number> {
  return (source: Observable<number>) =>
    source.pipe(
      mapIfSource<number, string, number>(
        (value) => value % 15 == 0 || value % 3 == 0 || value % 5 == 0,
        (value) => (value % 15 == 0 ? `FizzBuzz` : value % 3 === 0 ? 'Fizz' : 'Buzz'),
        (value) => value
     )
  );
}

// And now we use it in our code
fromNumber([1, 3, 5, 15, 16]).pipe(
  fizzbuzz(),
).subscribe() // 1, 'Fizz', 'Buzz', 'FizzBuzz', 16
Enter fullscreen mode Exit fullscreen mode

Hopefully you'll find these operators useful and feel free to leave feedback and suggestions.

Discussion

pic
Editor guide