Library update: RSLike@3.0.0. Better TS types. Symbols and more
What's new
this release makes a massive update. Just look at these changes!
std. Standard library.
All changes apply to Option
and Result
objects.
Symbols for Option and Result objects
Implements Symbol.iterator
, Symbol.asyncIterator
, Symbol.search
, Symbol.split
, Symbol.inspect
, Symbol.toPrimitive
, Symbol.toStringTag
Example:
// Before
const arr = Some([1, 2, 3]);
// error, for..of only supports with arrays
for (let el of arr) {
}
As a workaround, you may unwrap the value
// workaround
const arr = Some([1, 2, 3]);
// error, for..of now works with arrays
for (let el of arr.unwrap()) {
}
Now you can "just call for..of".
// now it works
const arr = Some([1, 2, 3]);
// error, for..of only supports with arrays
for (let el of arr) {
// works now!
}
Note: This method will only yeild if the Option
is Some
Note: throws UndefinedBehaviorError
for Some(value)
if value
is not implements Symbol.iterator
More examples with Symbols implementation can found in the wiki
Symbol.hasInstance for Some
, None
, Ok
and Err
functions
Now you can call instanceof
keyword for functions. From now you can skip importing Result
andOption
classes.
Example:
// Before
import { Ok, Err, Result } from "@rslike/std";
const a = Ok(3);
a instanceof Ok; // false
a instanceof Result; // true
// now, less imports
import { Ok, Err } from "@rslike/std";
const b = Ok(3);
b instanceof Ok; // true
b instanceof Err; // false
Err("Some Err") instanceof Err; // true
Advanced Types inferring
Add more complex TS types to understand have value or not.
Example:
// Before
const a = Some(3);
a.isSome(); // TS type: boolean
// now
a.isSome(); // TS type: true
double unwrapping in match function for Result<Option<T>, E>
From now you just can use match
a function with only 1 unwrapping.
If you're using Async
or Bind
functions - your result will be wrapped to Result<Option<_>>
. To unwrap this result you must use double matching.
Example (before - 67 lines):
import { Bind, match, Err } from "@rslike/std";
function divide(a: number, b: number) {
if (b === 0) Err("Divide to zero");
if (a === 0) Ok(0);
if (Number.isNaN(a) || Number.isNaN(b)) return undefined;
return a / b;
}
const binded = Bind(divide);
const fn1 = binded(1, 1); // Result<Option<number | undefined>, string>
const fn2 = binded(NaN, 1); // Result<Option<undefined>, string>
const res1 = match(
fn1, // or fn2
(res) => {
return match(
res,
(value) => console.log("value is:", value)
() => console.log("value is None"))
(err) => console.error(err);
);
console.log(res1); // value is: 1
console.log(res2); // value is None
Example (now - 27 lines):
import { Bind, match, Err } from "@rslike/std";
function divide(a: number, b: number) {
if (b === 0) Err("Divide to zero");
if (a === 0) Ok(0);
if (Number.isNaN(a) || Number.isNaN(b)) return undefined;
return a / b;
}
const binded = Bind(divide);
const fn1 = binded(1, 1); // Result<Option<number | undefined>, string>
const fn2 = binded(NaN, 1); // Result<Option<undefined>, string>
const res1 = match(
fn1, // or fn2
(value) => {
console.log("value is:", value);
},
(err) => {
if (err) console.error(err);
else console.log("value is None");
}
);
console.log(res1); // value is: 1
console.log(res2); // value is None
cmp. Comparison package
This package introduces custom Symbol
implementation in global objects.
Compare package now registered in the global scope Symbol.compare
, Symbol.partialEquals
and Symbol.equals
.
Symbol.compare
This symbol exists to compare 2 values - Self and another.
We recommend throwing UndefinedBehaviorError
for uncomparable types.
This method should return a number.
If method returns
1
(or more) -Self > another
If method returns
0
-Self == another
If method returns
-1
-Self < another
If method returns
NaN
- arguments have the same types but are not comparable to each other. E.g.NaN == NaN -> false
Symbol.compare
implemented for Number
, String
, Boolean
and Date
objects. You can implement this trait for any object and use it with primitives.
Examples
Built-in
(5)[Symbol.compare](3); // 5 > 3 returns 1
"qwe"[Symbol.compare]("asd"); // 'a' > 'q'. returns -1
Custom implementation
class A {
constructor(private readonly value: number) {}
[Symbol.compare](this, another: unknown) {
if (typeof another === "number") {
return this.value - another;
}
throw new Error("unsupported type");
}
}
const a = new A(5);
(5)[Symbol.compare](a); // A(5) - 5 = 0
Symbol.partialEquals
This method tests for this
and other
values to be equal, and is used by ==
(with type lossenes).
NOTE:other
always will be unknown
. Generic T
only helps when used with typescript. Perform checks on your side.
NOTE: partialEquals function is used this
to bind self result.
Symbol.partialEquals
implemented for Number
, String
, Boolean
and Date
objects. You can implement this trait for any object and use it with primitives.
Best practice
accept other
as unknown
type
use function declaration for this binding if you need to use an original object to compare
for objects - use partialEquals
since it can interpreted as "shallow" equality
Example
Built-in:
(5)[Symbol.partialEquals]("5"); // 5 == '5' -> true
Custom implementation
class A {
constructor(private readonly value: number) {}
[Symbol.partialEquals](this, another: unknown) {
if (typeof another === "number") {
return true;
}
throw new Error("unsupported type");
}
}
const a = new A(5);
(6)[Symbol.partialEquals](a); // A(6) always is true for number
Symbol.equals
Type for equality comparisons which are equivalence relations.
other always will be unknown
. Generic T
only helps when used with typescript. Perform checks on your side.
built-in objects(Number
, String
, Boolean
, Date
) will throw UndefinedBehaviorError error for an object with [Symbol.equals]
trait implementation but returns, not a boolean type
Best practice
Return "boolean" type without throwing an error
Example
Built-in
(6)[Symbol.equals]("6"); // 6 === '6' -> false
Custom implementation
import { type Eq, equals } from "@rslike/cmp";
enum BookFormat {
Paperback,
Hardback,
Ebook,
}
class Book implements Eq {
public isbn: boolean;
public format: BookFormat;
[Symbol.equals](other: Book) {
return other instanceof Book && this.isbn == other.isbn;
}
}
const book1 = new Book();
book1.isbn = true;
const book2 = new Book();
book2.isbn = true;
equals(book1, book2); // true
equals(book1, 5); // false
equals(book1, true); // false
Utility functions
compare
Called Symbol.compare to compare 2 arguments between each other and returns number.
In most cases, it returns 4 possible numeric values and can be interpreted as:
result is
>=1
- the first argument is more than incomingresult is
0
- arguments are the sameresult is
<=-1
- the second argument is more than incomingresult is
NaN
- arguments have the same types but are uncomparable between each other (e.g. comparingNaN
withNaN
givesNaN
, since we cannot compare 2NaN
s)
If neither of the arguments implements Symbol.compare trait - compareFn(3rd argument) will be called.
Throws
UndefinedBehaviorError
ifcompareFn
is not defined and neither of the arguments implements Symbol.compare traitUndefinedBehaviorError
ifcompareFn
is not a functionUndefinedBehaviorError
ifcompareFn
returns not a number type (e.g. string or boolean)
Example
import { compare } from "@rslike/cmp";
compare(2, 3); // -1, 3 > 2
compare(2, {
[Symbol.compare]() {
return 500;
},
}); // 500, object returns 500
compare(2, {
[Symbol.compare]() {
return 'hello world'';
},
}); // throws undefiend behavior. Symbol.compare should return a number
partialEquals
Partial equals. Same as ==(type loose comparison). You can interpretate it as "shallow" equal
Throws
UndefinedBehaviorError
for a and b objects without implementation Symbol.partialEquals trait or if Symbol.partialEquals trait returns not boolean type.
Example
import { partialEquals } from "@rslike/cmp";
partialEquals(5, "5"); // true
equals
Equals. Same as ===(type looseness comparison).
Called [Symbol.equals]
implementation for the first or another argument. If neither of the arguments implements [Symbol.equals]
trait then equalityFn
the argument will be called. Else - UndefinedBehaviorError
will be throws
Throws
— UndefinedBehaviorError
for a and b objects without implementation "Symbol.equals"
trait
— UndefinedBehaviorError
if [Symbol.equals]
trait returns not boolean type(e.g. string or number)
Types
Eq
type for checking equality. requires to implement [Symbol.equals]
Type for equality comparisons which are equivalence relations.
This means, that in addition to a == b
and a != b
being strict inverses, the equality must be (for all a, b and c):
reflexive: a == a
; symmetric: a == b
implies b == a
; and transitive: a == b
and b == c
implies a == c
. This property cannot be checked by the compiler, and therefore Eq
implies PartialEq
, and has equality
extra method.
Example
specify that your type implements Eq
.
enum BookFormat {
Paperback,
Hardback,
Ebook,
}
class Book implements Eq {
public isbn: number;
public format: BookFormat;
[Symbol.equals](another) {
return other instanceof Book && this.isbn == other.isbn;
}
}
Ord
Requires to implement Symbol.compare
trait
Example
import { type Ord } from "@rslike/cmp";
class MyArr<number> implements Ord {
readonly currentIndex: number;
[Symbol.compare](other: number) {
return this.currentIndex - other;
}
}