diff --git a/.changeset/sixty-news-begin.md b/.changeset/sixty-news-begin.md new file mode 100644 index 0000000000..fa666baa87 --- /dev/null +++ b/.changeset/sixty-news-begin.md @@ -0,0 +1,5 @@ +--- +"effect": patch +--- + +add BigInt.fromString and BigInt.fromNumber diff --git a/packages/effect/src/BigInt.ts b/packages/effect/src/BigInt.ts index 5a0305807e..ee7deccf31 100644 --- a/packages/effect/src/BigInt.ts +++ b/packages/effect/src/BigInt.ts @@ -547,10 +547,24 @@ export const multiplyAll = (collection: Iterable): bigint => { } /** - * Convers a bigint into a number + * Takes a `bigint` and returns an `Option` of `number`. + * + * If the `bigint` is outside the safe integer range for JavaScript (`Number.MAX_SAFE_INTEGER` + * and `Number.MIN_SAFE_INTEGER`), it returns `Option.none()`. Otherwise, it converts the `bigint` + * to a number and returns `Option.some(number)`. + * + * @param b - The `bigint` to be converted to a `number`. + * + * @example + * import { toNumber } from "effect/BigInt" + * import { Option } from "effect" + * + * assert.deepStrictEqual(toNumber(BigInt(42)), Option.some(42)) + * assert.deepStrictEqual(toNumber(BigInt(Number.MAX_SAFE_INTEGER) + BigInt(1)), Option.none()) + * assert.deepStrictEqual(toNumber(BigInt(Number.MIN_SAFE_INTEGER) - BigInt(1)), Option.none()) * - * @since 2.0.0 * @category conversions + * @since 2.0.0 */ export const toNumber = (b: bigint): Option.Option => { if (b > BigInt(Number.MAX_SAFE_INTEGER) || b < BigInt(Number.MIN_SAFE_INTEGER)) { @@ -558,3 +572,64 @@ export const toNumber = (b: bigint): Option.Option => { } return Option.some(Number(b)) } + +/** + * Takes a string and returns an `Option` of `bigint`. + * + * If the string is empty or contains characters that cannot be converted into a `bigint`, + * it returns `Option.none()`, otherwise, it returns `Option.some(bigint)`. + * + * @param s - The string to be converted to a `bigint`. + * + * @example + * import { fromString } from "effect/BigInt" + * import { Option } from "effect" + * + * assert.deepStrictEqual(fromString("42"), Option.some(BigInt(42))) + * assert.deepStrictEqual(fromString(" "), Option.none()) + * assert.deepStrictEqual(fromString("a"), Option.none()) + * + * @category conversions + * @since 2.4.12 + */ +export const fromString = (s: string): Option.Option => { + try { + return s.trim() === "" + ? Option.none() + : Option.some(BigInt(s)) + } catch (_) { + return Option.none() + } +} + +/** + * Takes a number and returns an `Option` of `bigint`. + * + * If the number is outside the safe integer range for JavaScript (`Number.MAX_SAFE_INTEGER` + * and `Number.MIN_SAFE_INTEGER`), it returns `Option.none()`. Otherwise, it attempts to + * convert the number to a `bigint` and returns `Option.some(bigint)`. + * + * @param n - The number to be converted to a `bigint`. + * + * @example + * import { fromNumber } from "effect/BigInt" + * import { Option } from "effect" + * + * assert.deepStrictEqual(fromNumber(42), Option.some(BigInt(42))) + * assert.deepStrictEqual(fromNumber(Number.MAX_SAFE_INTEGER + 1), Option.none()) + * assert.deepStrictEqual(fromNumber(Number.MIN_SAFE_INTEGER - 1), Option.none()) + * + * @category conversions + * @since 2.4.12 + */ +export const fromNumber = (n: number): Option.Option => { + if (n > Number.MAX_SAFE_INTEGER || n < Number.MIN_SAFE_INTEGER) { + return Option.none() + } + + try { + return Option.some(BigInt(n)) + } catch (_) { + return Option.none() + } +} diff --git a/packages/effect/src/Number.ts b/packages/effect/src/Number.ts index ae76dae486..eec227b6ea 100644 --- a/packages/effect/src/Number.ts +++ b/packages/effect/src/Number.ts @@ -400,7 +400,7 @@ export const sumAll = (collection: Iterable): number => { * @param collection - The collection of `number`s to multiply. * * @example - * import { multiplyAll } from 'effect/Number' + * import { multiplyAll } from "effect/Number" * * assert.deepStrictEqual(multiplyAll([2, 3, 4]), 24) * diff --git a/packages/effect/test/BigInt.test.ts b/packages/effect/test/BigInt.test.ts index 3a3017b042..4c70c04867 100644 --- a/packages/effect/test/BigInt.test.ts +++ b/packages/effect/test/BigInt.test.ts @@ -142,7 +142,43 @@ describe("BigInt", () => { }) it("toNumber", () => { - assert.deepStrictEqual(BigInt_.toNumber(1n), Option.some(1)) - assert.deepStrictEqual(BigInt_.toNumber(BigInt(Number.MAX_SAFE_INTEGER) + 1n), Option.none()) + assert.deepStrictEqual(BigInt_.toNumber(BigInt(Number.MAX_SAFE_INTEGER)), Option.some(Number.MAX_SAFE_INTEGER)) + assert.deepStrictEqual(BigInt_.toNumber(BigInt(Number.MAX_SAFE_INTEGER) + BigInt(1)), Option.none()) + assert.deepStrictEqual(BigInt_.toNumber(BigInt(Number.MIN_SAFE_INTEGER)), Option.some(Number.MIN_SAFE_INTEGER)) + assert.deepStrictEqual(BigInt_.toNumber(BigInt(Number.MIN_SAFE_INTEGER) - BigInt(1)), Option.none()) + assert.deepStrictEqual(BigInt_.toNumber(BigInt(0)), Option.some(0)) + assert.deepStrictEqual(BigInt_.toNumber(BigInt(42)), Option.some(42)) + assert.deepStrictEqual(BigInt_.toNumber(BigInt(-42)), Option.some(-42)) + }) + + it("fromString", () => { + assert.deepStrictEqual(BigInt_.fromString("NaN"), Option.none()) + assert.deepStrictEqual(BigInt_.fromString("Infinity"), Option.none()) + assert.deepStrictEqual(BigInt_.fromString("-Infinity"), Option.none()) + assert.deepStrictEqual(BigInt_.fromString("3.14"), Option.none()) + assert.deepStrictEqual(BigInt_.fromString("-3.14"), Option.none()) + assert.deepStrictEqual(BigInt_.fromString("1e3"), Option.none()) + assert.deepStrictEqual(BigInt_.fromString("1e-3"), Option.none()) + assert.deepStrictEqual(BigInt_.fromString(""), Option.none()) + assert.deepStrictEqual(BigInt_.fromString("a"), Option.none()) + assert.deepStrictEqual(BigInt_.fromString("42"), Option.some(BigInt(42))) + assert.deepStrictEqual(BigInt_.fromString("\n\r\t 42 \n\r\t"), Option.some(BigInt(42))) + }) + + it("fromNumber", () => { + assert.deepStrictEqual(BigInt_.fromNumber(Number.MAX_SAFE_INTEGER), Option.some(BigInt(Number.MAX_SAFE_INTEGER))) + assert.deepStrictEqual(BigInt_.fromNumber(Number.MAX_SAFE_INTEGER + 1), Option.none()) + assert.deepStrictEqual(BigInt_.fromNumber(Number.MIN_SAFE_INTEGER), Option.some(BigInt(Number.MIN_SAFE_INTEGER))) + assert.deepStrictEqual(BigInt_.fromNumber(Number.MIN_SAFE_INTEGER - 1), Option.none()) + assert.deepStrictEqual(BigInt_.fromNumber(Infinity), Option.none()) + assert.deepStrictEqual(BigInt_.fromNumber(-Infinity), Option.none()) + assert.deepStrictEqual(BigInt_.fromNumber(NaN), Option.none()) + assert.deepStrictEqual(BigInt_.fromNumber(1e100), Option.none()) + assert.deepStrictEqual(BigInt_.fromNumber(-1e100), Option.none()) + assert.deepStrictEqual(BigInt_.fromNumber(3.14), Option.none()) + assert.deepStrictEqual(BigInt_.fromNumber(-3.14), Option.none()) + assert.deepStrictEqual(BigInt_.fromNumber(0), Option.some(BigInt(0))) + assert.deepStrictEqual(BigInt_.fromNumber(42), Option.some(BigInt(42))) + assert.deepStrictEqual(BigInt_.fromNumber(-42), Option.some(BigInt(-42))) }) })