ts-data-forge
Version:
[](https://www.npmjs.com/package/ts-data-forge) [](https://www.npmjs.com/package/ts-data-forge) [ number
* types.
*
* This namespace provides factory functions and type utilities for building
* type-safe numeric operations with compile-time constraints. It serves as
* the foundation for all branded number types in the library, including:
*
* - Integer types (Int, SafeInt, Int8, Int16, Int32)
* - Unsigned types (UInt, UInt8, UInt16, UInt32)
* - Constrained types (NonZero, NonNegative, Positive)
* - Range-bounded types
*
* The utilities handle:
*
* - Type validation and narrowing
* - Arithmetic operations that preserve type constraints
* - Automatic clamping for bounded types
* - Random number generation within type bounds
*
* @internal This namespace is not part of the public API
*/
namespace RefinedNumberUtils {
type UnknownNumberBrand = ChangeBaseBrand<UnknownBrand, number>;
/**
* Converts a branded number type to include the Int brand.
*
* @template N - A branded number type
* @internal
*/
export type ToInt<N extends UnknownNumberBrand> = IntersectBrand<N, Int>;
type ToNonZero<N extends UnknownNumberBrand> = IntersectBrand<N, NonZeroNumber>;
type ToNonZeroIntWithSmallInt<N extends Int> = WithSmallInt<CastToInt<ToNonZero<N>>>;
/**
* Converts a branded number type to include the NonNegativeNumber brand.
*
* @template N - A branded number type
* @internal
*/
export type ToNonNegative<N extends UnknownNumberBrand> = IntersectBrand<N, NonNegativeNumber>;
/**
* Removes the non-zero brand constraint from a branded number type. Used
* when operations may produce zero values.
*
* @template N - A branded number type
* @internal
*/
export type RemoveNonZeroBrandKey<N extends UnknownNumberBrand> = Brand<GetBrandValuePart<N>, RelaxedExclude<UnwrapBrandTrueKeys<N>, '!=0'> & string, UnwrapBrandFalseKeys<N> & string>;
type CastToInt<N> = N extends Int ? N : never;
/**
* Generates a type-safe API for a branded number type based on its
* characteristics.
*
* This type dynamically constructs an object type with appropriate methods
* based on the number class. For example:
*
* - Integer types don't get floor/ceil/round methods
* - Non-negative types don't get abs method
* - Range-bounded types get MIN_VALUE/MAX_VALUE constants
*
* @template N - The branded number type
* @template classes - Union of characteristics: 'int' | 'non-negative' |
* 'positive' | 'range'
* @internal
*/
export type NumberClass<N extends UnknownNumberBrand, classes extends 'int' | 'non-negative' | 'positive' | 'range'> = ('int' extends classes ? unknown : 'positive' extends classes ? Readonly<{
floor: (x: N, y: N) => RemoveNonZeroBrandKey<ToInt<N>>;
ceil: (x: N, y: N) => ToInt<N>;
round: (x: N, y: N) => RemoveNonZeroBrandKey<ToInt<N>>;
}> : Readonly<{
floor: (x: N, y: N) => ToInt<N>;
ceil: (x: N, y: N) => ToInt<N>;
round: (x: N, y: N) => ToInt<N>;
}>) & ('non-negative' extends classes ? Readonly<{
MIN_VALUE: number;
clamp: (a: number) => N;
}> : unknown) & ('non-negative' extends classes ? unknown : 'positive' extends classes ? unknown : Readonly<{
abs: (x: N) => ToNonNegative<N>;
}>) & ('positive' extends classes ? Readonly<{
MIN_VALUE: number;
clamp: (a: number) => N;
}> : unknown) & ('range' extends classes ? Readonly<{
MIN_VALUE: number;
MAX_VALUE: number;
clamp: (a: number) => N;
}> : unknown) & Readonly<{
is: (a: number) => a is N;
min: (...values: readonly N[]) => N;
max: (...values: readonly N[]) => N;
random: (min: N, max: N) => N;
pow: (x: N, y: N) => N;
add: (x: N, y: N) => N;
sub: (x: N, y: N) => N;
mul: (x: N, y: N) => N;
div: (x: N, y: ToNonZero<N>) => N;
}>;
type OperatorsForInteger<ElementType extends Int, MIN_VALUE extends number, MAX_VALUE extends number, ElementTypeWithSmallInt extends WithSmallInt<ElementType> = WithSmallInt<ElementType>> = Readonly<{
MIN_VALUE: MIN_VALUE;
MAX_VALUE: MAX_VALUE;
is: (a: number) => a is ElementType;
abs: (x: ElementTypeWithSmallInt) => ToNonNegative<ElementType>;
min: (...values: readonly ElementTypeWithSmallInt[]) => ElementType;
max: (...values: readonly ElementTypeWithSmallInt[]) => ElementType;
pow: (x: ElementTypeWithSmallInt, y: ElementTypeWithSmallInt) => ElementType;
add: (x: ElementTypeWithSmallInt, y: ElementTypeWithSmallInt) => ElementType;
sub: (x: ElementTypeWithSmallInt, y: ElementTypeWithSmallInt) => ElementType;
mul: (x: ElementTypeWithSmallInt, y: ElementTypeWithSmallInt) => ElementType;
div: (x: ElementTypeWithSmallInt, y: ToNonZeroIntWithSmallInt<ElementType>) => ElementType;
random: (min?: ElementTypeWithSmallInt, max?: ElementTypeWithSmallInt) => ElementType;
randomNonZero: (min?: ElementTypeWithSmallInt, max?: ElementTypeWithSmallInt) => ElementType;
castType: <N extends number>(x: N) => ElementType & N;
clamp: TypeEq<MAX_VALUE | MIN_VALUE, undefined> extends true ? undefined : (x: number) => ElementType;
}>;
/**
* Factory function that creates a complete set of type-safe operations for
* integer types.
*
* This function generates:
*
* - Type guards and validators
* - Arithmetic operations that preserve type constraints
* - Utility functions (min, max, abs, random)
* - Automatic clamping for bounded types
*
* All operations ensure results remain within the type's constraints, using
* clamping when bounds are specified.
*
* @example
*
* ```ts
* const intOps = TsDataForgeInternals.RefinedNumberUtils.operatorsForInteger<
* SafeInt,
* number,
* number
* >({
* integerOrSafeInteger: 'SafeInteger',
* MIN_VALUE: Number.MIN_SAFE_INTEGER,
* MAX_VALUE: Number.MAX_SAFE_INTEGER,
* typeNameInMessage: 'SafeInt',
* } as const);
*
* const six = intOps.castType(6);
*
* const four = intOps.castType(4);
*
* const sum = intOps.add(six, four);
*
* const difference = intOps.sub(six, four);
*
* const product = intOps.mul(six, four);
*
* const quotient = intOps.div(six, intOps.castType(2));
*
* const roundedClamp = intOps.clamp(1.5);
*
* const randomValue = intOps.random();
*
* assert.isTrue(sum === 10);
*
* assert.isTrue(difference === 2);
*
* assert.isTrue(product === 24);
*
* assert.isTrue(quotient === 3);
*
* assert.isTrue(roundedClamp === 2);
*
* assert.isTrue(Number.isSafeInteger(randomValue));
* ```
*
* @template ElementType - The integer branded type
* @template MIN_VALUE - Optional minimum value for bounded types
* @template MAX_VALUE - Optional maximum value for bounded types
* @param config - Configuration object
* @param config.integerOrSafeInteger - Whether to use Number.isInteger or
* Number.isSafeInteger
* @param config.nonZero - If true, excludes zero from valid values
* @param config.MIN_VALUE - Minimum valid value (inclusive)
* @param config.MAX_VALUE - Maximum valid value (inclusive)
* @param config.typeNameInMessage - Human-readable type name for error
* messages
* @returns Object containing all type-safe operations for the integer type
* @internal
*/
export const operatorsForInteger: <ElementType extends Int, MIN_VALUE extends number, MAX_VALUE extends number>({ integerOrSafeInteger, nonZero, MIN_VALUE, MAX_VALUE, typeNameInMessage, }: Readonly<{
integerOrSafeInteger: "Integer" | "SafeInteger";
nonZero?: boolean;
MIN_VALUE: MIN_VALUE;
MAX_VALUE: MAX_VALUE;
typeNameInMessage: string;
}>) => OperatorsForInteger<ElementType, MIN_VALUE, MAX_VALUE>;
type OperatorsForFloat<ElementType extends UnknownNumberBrand, MIN_VALUE extends number | undefined, MAX_VALUE extends number | undefined> = Readonly<{
MIN_VALUE: MIN_VALUE;
MAX_VALUE: MAX_VALUE;
is: (a: number) => a is ElementType;
abs: (x: ElementType) => ToNonNegative<ElementType>;
min: (...values: readonly ElementType[]) => ElementType;
max: (...values: readonly ElementType[]) => ElementType;
pow: (x: ElementType, y: ElementType) => ElementType;
add: (x: ElementType, y: ElementType) => ElementType;
sub: (x: ElementType, y: ElementType) => ElementType;
mul: (x: ElementType, y: ElementType) => ElementType;
div: (x: ElementType, y: ToNonZero<ElementType>) => ElementType;
random: (min?: ElementType, max?: ElementType) => ElementType;
randomNonZero: (min?: ElementType, max?: ElementType) => ElementType;
castType: <N extends number>(x: N) => ElementType & N;
clamp: TypeEq<MAX_VALUE | MIN_VALUE, undefined> extends true ? undefined : (x: number) => ElementType;
}>;
/**
* Factory function that creates a complete set of type-safe operations for
* floating-point types.
*
* This function generates:
*
* - Type guards and validators (checking for finite values)
* - Arithmetic operations that preserve type constraints
* - Utility functions (min, max, abs, random)
* - Automatic clamping for bounded types
*
* All operations ensure results remain finite and within any specified
* bounds. Division by zero is prevented through type constraints.
*
* @example
*
* ```ts
* const floatOps = TsDataForgeInternals.RefinedNumberUtils.operatorsForFloat<
* PositiveFiniteNumber,
* number,
* number
* >({
* nonZero: true,
* MIN_VALUE: Number.MIN_VALUE,
* MAX_VALUE: Number.MAX_VALUE,
* typeNameInMessage: 'PositiveFiniteNumber',
* } as const);
*
* const fortyTwo = floatOps.castType(42.5);
*
* const seven = floatOps.castType(7.5);
*
* const sum = floatOps.add(fortyTwo, seven);
*
* const ratio = floatOps.div(sum, floatOps.castType(10));
*
* const clamped = floatOps.clamp(0);
*
* const boundedRandom = floatOps.random(
* floatOps.castType(10),
* floatOps.castType(20),
* );
*
* const nonZeroRandom = floatOps.randomNonZero();
*
* assert.isTrue(sum === 50);
*
* assert.isTrue(ratio === 5);
*
* assert.isTrue(clamped >= Number.MIN_VALUE);
*
* assert.isTrue(boundedRandom >= 10 && boundedRandom <= 20);
*
* assert.isTrue(nonZeroRandom > 0);
* ```
*
* @template ElementType - The floating-point branded type
* @template MIN_VALUE - Optional minimum value for bounded types
* @template MAX_VALUE - Optional maximum value for bounded types
* @param config - Configuration object
* @param config.nonZero - If true, excludes zero from valid values
* @param config.MIN_VALUE - Minimum valid value (inclusive)
* @param config.MAX_VALUE - Maximum valid value (inclusive)
* @param config.typeNameInMessage - Human-readable type name for error
* messages
* @returns Object containing all type-safe operations for the
* floating-point type
* @internal
*/
export const operatorsForFloat: <ElementType extends UnknownNumberBrand, MIN_VALUE extends number, MAX_VALUE extends number>({ nonZero, MIN_VALUE, MAX_VALUE, typeNameInMessage, }: Readonly<{
nonZero?: boolean;
MIN_VALUE: MIN_VALUE;
MAX_VALUE: MAX_VALUE;
typeNameInMessage: string;
}>) => OperatorsForFloat<ElementType, MIN_VALUE, MAX_VALUE>;
export {};
}
}
//# sourceMappingURL=refined-number-utils.d.mts.map