Foundation types, utilities, and interfaces that underpin the entire Fable library. This module provides the essential building blocks for F# type system implementation in JavaScript.
The Util module provides core interfaces, utility functions, and foundational operations used throughout the library.
// Core comparison interface
interface IComparer<T> {
Compare(x: T, y: T): number;
}
// Object comparison capability
interface IComparable<T> {
CompareTo(x: T): number;
}
// Equality comparison interface
interface IEqualityComparer<T> {
Equals(x: T, y: T): boolean;
GetHashCode(x: T): number;
}
// Object equality capability
interface IEquatable<T> {
Equals(x: T): boolean;
}// Disposable resource interface
interface IDisposable {
Dispose(): void;
}// Extended Date interface with kind
interface IDateTime extends Date {
kind?: DateKind;
}
// DateTimeOffset interface
interface IDateTimeOffset extends Date {
offset?: number;
}
// Date kind enumeration
enum DateKind {
Unspecified = 0,
UTC = 1,
Local = 2
}Generic comparer implementation with custom comparison functions.
class Comparer<T> implements IComparer<T> {
constructor(f?: (x: T, y: T) => number);
Compare(x: T, y: T): number;
}
// Usage
import { Comparer } from "fable-library/Util.js";
const stringComparer = new Comparer<string>((x, y) => x.localeCompare(y));
const result = stringComparer.Compare("apple", "banana"); // -1Lazy evaluation implementation for deferred computation.
class Lazy<T> {
constructor(factory: () => T);
// Properties
readonly Value: T; // Computed value (triggers evaluation)
readonly IsValueCreated: boolean; // Whether value has been computed
isValueCreated: boolean;
factory: () => T;
}
// Usage
import { Lazy } from "fable-library/Util.js";
const lazyValue = new Lazy(() => {
console.log("Computing...");
return 42;
});
console.log(lazyValue.IsValueCreated); // false
console.log(lazyValue.Value); // "Computing..." then 42
console.log(lazyValue.IsValueCreated); // trueStatic utility for object reference management.
class ObjectRef {
static id(o: any): number;
}
// Usage
import { ObjectRef } from "fable-library/Util.js";
const obj1 = { name: "test" };
const obj2 = { name: "test" };
console.log(ObjectRef.id(obj1)); // Unique ID
console.log(ObjectRef.id(obj2)); // Different ID// Extend target object with sources
function extend(target: any, ...sources: any[]): any;
// Check if object implements IDisposable
function isDisposable(x: any): boolean;
// Create comparer from equality comparer
function comparerFromEqualityComparer<T>(comparer: IEqualityComparer<T>): Comparer<T>;
// Usage
import { extend, isDisposable } from "fable-library/Util.js";
const target = { a: 1 };
const source = { b: 2, c: 3 };
const result = extend(target, source); // { a: 1, b: 2, c: 3 }// Check if Map contains value
function containsValue<K, V>(v: V, map: Map<K, V>): boolean;
// Try get value with default
function tryGetValue<K, V>(map: Map<K, V>, key: K, defaultValue: V): [boolean, V];
// Add value to Set, returns true if added
function addToSet<T>(v: T, set: Set<T>): boolean;
// Count items in iterable
function count<T>(col: Iterable<T>): number;
// Clear collection
function clear<T>(col: Iterable<T>): void;
// Dictionary operations
function addToDict<K, V>(dict: Map<K, V>, k: K, v: V): void;
function getItemFromDict<K, V>(map: Map<K, V>, key: K): V;
// Usage
import { containsValue, tryGetValue, count } from "fable-library/Util.js";
const map = new Map([["a", 1], ["b", 2]]);
console.log(containsValue(2, map)); // true
const [found, value] = tryGetValue(map, "c", 0);
console.log(found, value); // false, 0
console.log(count([1, 2, 3, 4])); // 4// Universal equality check
function equals(x: any, y: any): boolean;
// Universal comparison
function compare(x: any, y: any): number;
// Specialized comparisons
function compareDates(x: Date | IDateTime | IDateTimeOffset, y: Date | IDateTime | IDateTimeOffset): number;
function comparePrimitives(x: any, y: any): number;
function compareArrays<T>(x: ArrayLike<T>, y: ArrayLike<T>): number;
function compareArraysWith<T>(x: ArrayLike<T>, y: ArrayLike<T>, comp: (x: T, y: T) => number): number;
function compareObjects(x: { [k: string]: any }, y: { [k: string]: any }): number;
// Equality checks
function equalArrays<T>(x: ArrayLike<T>, y: ArrayLike<T>): boolean;
function equalArraysWith<T>(x: ArrayLike<T>, y: ArrayLike<T>, eq: (x: T, y: T) => boolean): boolean;
// Usage
import { equals, compare, equalArrays } from "fable-library/Util.js";
console.log(equals([1, 2, 3], [1, 2, 3])); // true
console.log(compare("apple", "banana")); // -1
console.log(equalArrays([1, 2], [1, 2])); // true// String hash computation
function stringHash(s: string): number;
// Number hash computation
function numberHash(x: number): number;
// Combine multiple hash codes
function combineHashCodes(hashes: number[]): number;
// Identity-based hash
function identityHash(x: any): number;
// Structural hash (deep)
function structuralHash(x: any): number;
// Usage
import { stringHash, combineHashCodes, structuralHash } from "fable-library/Util.js";
const hash1 = stringHash("hello");
const hash2 = stringHash("world");
const combined = combineHashCodes([hash1, hash2]);
const objHash = structuralHash({ a: 1, b: [2, 3] });// Check if value is array
function isArray(x: any): boolean;
// Check if value is iterable
function isIterable(x: any): boolean;
// Usage
import { isArray, isIterable } from "fable-library/Util.js";
console.log(isArray([1, 2, 3])); // true
console.log(isIterable("hello")); // true
console.log(isIterable(new Set())); // true// Rounding with precision
function round(value: number, digits?: number): number;
// Sign function
function sign(x: number): number;
// Random number generation
function randomNext(min: number, max: number): number;
function randomBytes(buffer: Uint8Array): void;
// Min/Max with custom comparer
function min<T>(comparer: (x: T, y: T) => number, x: T, y: T): T;
function max<T>(comparer: (x: T, y: T) => number, x: T, y: T): T;
// Usage
import { round, randomNext, min } from "fable-library/Util.js";
console.log(round(3.14159, 2)); // 3.14
console.log(randomNext(1, 10)); // Random number between 1-10
const minValue = min((x, y) => x - y, 5, 3); // 3// Ignore function (discard value)
function ignore(x: any): void;
// Atom creation (mutable reference)
function createAtom<T>(value: T): (v?: T) => T | void;
// Function currying/uncurrying (arity 2-8)
function uncurry(arity: number, f: Function): Function;
function curry(arity: number, f: Function): Function;
function partialApply(arity: number, f: Function, args: any[]): any;
// Usage
import { ignore, createAtom, curry } from "fable-library/Util.js";
ignore(someComputationResult); // Discard result
const atom = createAtom(42);
console.log(atom()); // 42
atom(100);
console.log(atom()); // 100
const add = (x: number, y: number) => x + y;
const curriedAdd = curry(2, add);
const add5 = curriedAdd(5);
console.log(add5(3)); // 8// Create object from fields
function createObj(fields: Iterable<any>, caseRule?: number): { [k: string]: any };
// JavaScript options pattern
function jsOptions(mutator: (x: object) => void): object;
// Usage
import { createObj, jsOptions } from "fable-library/Util.js";
const obj = createObj([["name", "John"], ["age", 30]]);
// { name: "John", age: 30 }
const options = jsOptions(opt => {
opt.timeout = 5000;
opt.retries = 3;
});// URI encoding/decoding
function unescapeDataString(s: string): string;
function escapeDataString(s: string): string;
function escapeUriString(s: string): string;
// Usage
import { escapeDataString, unescapeDataString } from "fable-library/Util.js";
const encoded = escapeDataString("hello world"); // "hello%20world"
const decoded = unescapeDataString(encoded); // "hello world"// Create lazy value from computed value
function lazyFromValue<T>(v: T): Lazy<T>;
// Usage
import { lazyFromValue } from "fable-library/Util.js";
const lazy = lazyFromValue(42);
console.log(lazy.Value); // 42 (no computation)
console.log(lazy.IsValueCreated); // true// Pad number with leading zeros
function padWithZeros(i: number, length: number): string;
// Pad number with zeros on both sides
function padLeftAndRightWithZeros(i: number, lengthLeft: number, lengthRight: number): string;
// Get offset from date objects
function dateOffset(date: IDateTime | IDateTimeOffset): number;
// Convert integers to string with radix support
function int16ToString(i: number, radix?: number): string;
function int32ToString(i: number, radix?: number): string;
// Usage
import { padWithZeros, int16ToString, dateOffset } from "fable-library/Util.js";
console.log(padWithZeros(42, 5)); // "00042"
console.log(int16ToString(255, 16)); // "ff"
console.log(int32ToString(-1, 16)); // "ffffffff"
// Date offset handling
const dateTime: IDateTime = new Date();
dateTime.kind = DateKind.Local;
const offset = dateOffset(dateTime); // Local timezone offsetFundamental types and classes for the F# type system implementation in JavaScript.
Base object type for all F# objects with universal operations.
class SystemObject {
toString(): string;
GetHashCode(): number;
Equals(other: any): boolean;
}
// Usage
import { SystemObject } from "fable-library/Types.js";
class MyClass extends SystemObject {
constructor(public value: number) {
super();
}
}
const obj = new MyClass(42);
console.log(obj.toString()); // Object representation
console.log(obj.GetHashCode()); // Hash codeF# immutable linked list implementation.
class List<T> extends SystemObject {
constructor(head: T, tail: List<T> | null);
toString(): string;
toJSON(): T[];
GetHashCode(): number;
Equals(other: any): boolean;
CompareTo(other: any): number;
// Iterator support
[Symbol.iterator](): Iterator<T>;
}
// Usage
import { List } from "fable-library/Types.js";
// Create list: [1, 2, 3]
const list = new List(1, new List(2, new List(3, null)));
// Iterate
for (const item of list) {
console.log(item); // 1, 2, 3
}
// Convert to array
const array = list.toJSON(); // [1, 2, 3]F# discriminated union base class.
class Union extends SystemObject {
constructor(tag: number, name: string, ...fields: any[]);
tag: number;
name: string;
fields: any[];
toString(): string;
toJSON(): any;
GetHashCode(): number;
Equals(other: any): boolean;
CompareTo(other: any): number;
}
// Usage - Custom union type
import { Union } from "fable-library/Types.js";
class Shape extends Union {
static Circle(radius: number): Shape {
return new Shape(0, "Circle", radius);
}
static Rectangle(width: number, height: number): Shape {
return new Shape(1, "Rectangle", width, height);
}
}
const circle = Shape.Circle(5);
const rect = Shape.Rectangle(10, 20);
console.log(circle.tag); // 0
console.log(circle.name); // "Circle"
console.log(circle.fields); // [5]F# record base type with structural equality.
class Record extends SystemObject {
toString(): string;
toJSON(): any;
GetHashCode(): number;
Equals(other: any): boolean;
CompareTo(other: any): number;
}
// Usage - Custom record type
import { Record } from "fable-library/Types.js";
class Person extends Record {
constructor(public name: string, public age: number) {
super();
}
}
const person1 = new Person("John", 30);
const person2 = new Person("John", 30);
console.log(person1.Equals(person2)); // true (structural equality)F# reference cell for mutable references.
class FSharpRef<T> {
constructor(contents: T);
contents: T;
}
// Usage
import { FSharpRef } from "fable-library/Types.js";
const ref = new FSharpRef(42);
console.log(ref.contents); // 42
ref.contents = 100;
console.log(ref.contents); // 100Base exception type for error handling.
class Exception extends Error {
constructor(msg?: string);
stack: string;
message: string;
}
// Usage
import { Exception } from "fable-library/Types.js";
throw new Exception("Something went wrong");F# exception base with F# object capabilities.
class FSharpException extends Exception {
toString(): string;
toJSON(): any;
GetHashCode(): number;
Equals(other: any): boolean;
CompareTo(other: any): number;
}Specialized exception for pattern match failures.
class MatchFailureException extends FSharpException {
constructor(arg1: any, arg2: number, arg3: number);
arg1: any; // Source file/expression
arg2: number; // Line number
arg3: number; // Column number
message: string;
}
// Usage - thrown by generated F# pattern matching code
import { MatchFailureException } from "fable-library/Types.js";
// This would be generated by F# compiler
function matchValue(x: any): string {
if (x.tag === 0) return "Case0";
if (x.tag === 1) return "Case1";
throw new MatchFailureException("pattern.fs", 10, 5);
}Base attribute type for F# attributes.
class Attribute extends SystemObject {}
// Usage - Custom attribute
import { Attribute } from "fable-library/Types.js";
class ObsoleteAttribute extends Attribute {
constructor(public message?: string) {
super();
}
}// Declare F# type with inheritance
function declare(cons: Function, superClass?: Function): Function;
// Create anonymous record
function anonRecord(o: any): any;
// Check if value is exception
function isException(x: any): boolean;
// Usage
import { declare, anonRecord, isException } from "fable-library/Types.js";
// Anonymous record
const record = anonRecord({ name: "John", age: 30 });
// Check exception
console.log(isException(new Error())); // true
console.log(isException("not an error")); // falseF# option, choice, and result type implementations providing safe null handling and error management.
Wrapper for non-null optional values.
class Some<T> {
constructor(value: T);
value: T;
toString(): string;
toJSON(): T;
GetHashCode(): number;
Equals(other: any): boolean;
CompareTo(other: any): number;
}
// Usage
import { Some } from "fable-library/Option.js";
const someValue = new Some(42);
console.log(someValue.value); // 42
console.log(someValue.toString()); // "Some(42)"// Create Some value
function some<T>(x: T): Some<T> | null;
// Extract value from option
function value<T>(x: Some<T> | null, acceptNull?: boolean): T;
// Provide default value for None
function defaultArg<T>(arg: Some<T> | null, defaultValue: T, f?: Function): T;
// Provide default value via thunk
function defaultArgWith<T>(arg: Some<T> | null, defThunk: () => T): T;
// Filter option based on predicate
function filter<T>(predicate: (x: T) => boolean, arg: Some<T> | null): Some<T> | null;
// Usage
import { some, value, defaultArg, filter } from "fable-library/Option.js";
const opt1 = some(42);
const opt2 = null; // None
console.log(value(opt1)); // 42
// console.log(value(opt2)); // Would throw
console.log(defaultArg(opt1, 0)); // 42
console.log(defaultArg(opt2, 0)); // 0
const filtered = filter(x => x > 40, opt1); // Some(42)
const filtered2 = filter(x => x > 50, opt1); // null (None)F# choice type for handling multiple possible value types.
class Choice extends Union {
constructor(tag: number, name: string, field: any);
}
// Choice creation functions
function choice1<T>(x: T): Choice; // Choice1Of2
function choice2<T>(x: T): Choice; // Choice2Of2
// Choice extraction functions
function tryValueIfChoice1(x: Choice): any;
function tryValueIfChoice2(x: Choice): any;
// Usage
import { choice1, choice2, tryValueIfChoice1, tryValueIfChoice2 } from "fable-library/Option.js";
// Create choices
const success = choice1("Operation succeeded");
const error = choice2(new Error("Operation failed"));
// Extract values
const successValue = tryValueIfChoice1(success); // "Operation succeeded"
const errorValue = tryValueIfChoice2(error); // Error object
console.log(tryValueIfChoice1(error)); // null
console.log(tryValueIfChoice2(success)); // nullF# result type for error handling with Ok/Error cases.
class Result extends Union {
constructor(tag: number, name: string, field: any);
}
// Result creation functions
function ok<T>(x: T): Result; // Ok case
function error<T>(x: T): Result; // Error case
// Result transformation functions
function mapOk<T, U>(f: (x: T) => U, result: Result): Result;
function mapError<T, U>(f: (x: T) => U, result: Result): Result;
function bindOk<T, U>(f: (x: T) => Result, result: Result): Result;
// Usage
import { ok, error, mapOk, mapError, bindOk } from "fable-library/Option.js";
// Create results
const successResult = ok(42);
const errorResult = error("Something went wrong");
// Transform Ok values
const doubled = mapOk(x => x * 2, successResult); // Ok(84)
const stillError = mapOk(x => x * 2, errorResult); // Still Error
// Transform Error values
const betterError = mapError(msg => `Error: ${msg}`, errorResult);
// Chain operations (only if Ok)
const chained = bindOk(x => {
if (x > 40) return ok(x.toString());
else return error("Value too small");
}, successResult); // Ok("42")import { some, value, defaultArg } from "fable-library/Option.js";
// Convert nullable to option
function toOption<T>(nullable: T | null | undefined): Some<T> | null {
return nullable != null ? some(nullable) : null;
}
// Safe dictionary lookup
function safeDictLookup<K, V>(dict: Map<K, V>, key: K): Some<V> | null {
return dict.has(key) ? some(dict.get(key)!) : null;
}
// Chain operations safely
const dict = new Map([["a", 1], ["b", 2]]);
const result = safeDictLookup(dict, "a");
const doubled = result ? some(value(result) * 2) : null;
console.log(doubled ? value(doubled) : 0); // 2import { ok, error, bindOk, mapOk } from "fable-library/Option.js";
// Pipeline of operations that can fail
function divide(x: number, y: number): Result {
return y !== 0 ? ok(x / y) : error("Division by zero");
}
function sqrt(x: number): Result {
return x >= 0 ? ok(Math.sqrt(x)) : error("Negative number");
}
// Chain operations
const result = bindOk(x => sqrt(x), divide(16, 4)); // Ok(2)
const error_result = bindOk(x => sqrt(x), divide(16, 0)); // Error("Division by zero")
const error_result2 = bindOk(x => sqrt(x), divide(-16, 4)); // Error("Negative number")