UNPKG

@rickosborne/rebound

Version:

Rick Osborne's utilities for working with bounded numbers

204 lines 8.75 kB
export declare const BOUNDS: unique symbol; export type NegInfinityLabel = "-∞"; export type PosInfinityLabel = "+∞"; export type NegInfinity = number & { infinity: NegInfinityLabel; }; export type PosInfinity = number & { infinity: PosInfinityLabel; }; export type InfinityBound = number & { infinity: string; }; export type LowerInclusive = "["; export type LowerExclusive = "("; export type UpperInclusive = "]"; export type UpperExclusive = ")"; export declare const LOWER_IN: LowerInclusive; export declare const LOWER_EX: LowerExclusive; export declare const UPPER_IN: UpperInclusive; export declare const UPPER_EX: UpperExclusive; export interface BoundIsInclusive { readonly [LOWER_IN]: true; readonly [LOWER_EX]: false; readonly [UPPER_IN]: true; readonly [UPPER_EX]: false; } export type UpperInEx = UpperInclusive | UpperExclusive; export type LowerInEx = LowerInclusive | LowerExclusive; export type LowerInExFrom<B extends boolean> = boolean extends B ? never : true extends B ? LowerInclusive : LowerExclusive; export type UpperInExFrom<B extends boolean> = boolean extends B ? never : true extends B ? UpperInclusive : UpperExclusive; export type IsInclusive<Inc extends UpperInEx | LowerInEx> = BoundIsInclusive[Inc]; export type IntegerSet = "int"; export type RealSet = "real"; export declare const INT_SET: IntegerSet; export declare const REAL_SET: RealSet; export type NumberSet = IntegerSet | RealSet; export interface NumberSetIsInt { [INT_SET]: true; [REAL_SET]: false; } export type NumberSetFrom<B extends boolean> = boolean extends B ? never : true extends B ? IntegerSet : RealSet; export type IsIntFrom<Int extends NumberSet> = NumberSetIsInt[Int]; export interface AnyBoundsConfig { int?: NumberSet | undefined; lower?: number | undefined; lowerInc?: LowerInEx | undefined; range?: string | undefined; upper?: number | undefined; upperInc?: UpperInEx | undefined; } export interface BoundsConfig<LowerInc extends LowerInEx, Lower extends number, Int extends NumberSet, Upper extends number, UpperInc extends UpperInEx> extends AnyBoundsConfig { int: Int; lower: Lower; lowerInc: LowerInc; upper: Upper; upperInc: UpperInc; } export interface DefinedBounds { int: NumberSet; lower: number; lowerInc: LowerInEx; upper: number; upperInc: UpperInEx; } export interface CheckedBounds { isInt: boolean; isLowerInc: boolean; isUpperInc: boolean; lower: number; upper: number; } export interface DefinedFromChecked<IsLowerInc extends boolean, Lower extends number, IsInt extends boolean, Upper extends number, IsUpperInc extends boolean> extends DefinedBounds { int: NumberSetFrom<IsInt>; lower: Lower; lowerInc: LowerInExFrom<IsLowerInc>; upper: Upper; upperInc: UpperInExFrom<IsUpperInc>; } export interface DefinedFromCheckedConfig<Config extends CheckedBounds> extends DefinedFromChecked<Config["isLowerInc"], Config["lower"], Config["isInt"], Config["upper"], Config["isUpperInc"]> { } export interface CheckedBoundsConfig<IsLowerInc extends boolean, Lower extends number, IsInt extends boolean, Upper extends number, IsUpperInc extends boolean> { isInt: IsInt; lower: Lower; isLowerInc: IsLowerInc; upper: Upper; isUpperInc: IsUpperInc; } export interface RangedBounds extends DefinedBounds { range: string; } export interface TypedBounds extends RangedBounds { typeName: string; } export interface TypedCheckedBounds extends CheckedBounds { label: string; typeName: string; } export declare const TYPED_BOUNDS_KEYS: (keyof TypedBounds)[]; export declare const TYPED_CHECKED_BOUNDS_KEYS: (keyof TypedCheckedBounds)[]; export type BoundsLabel<C extends DefinedBounds> = `${C extends { lowerInc: infer LowerInc extends LowerInEx; } ? LowerInc : never}${C extends { lower: NegInfinity; } ? NegInfinityLabel : C extends { lower: infer Lower extends number; } ? number extends Lower ? never : `${Lower}` : never}${C extends { int: infer Int extends NumberSet; } ? Int extends IntegerSet ? ".." : "," : never}${C extends { upper: PosInfinity; } ? PosInfinityLabel : C extends { upper: infer Upper extends number; } ? number extends Upper ? never : `${Upper}` : never}${C extends { upperInc: infer UpperInc extends UpperInEx; } ? UpperInc : never}`; export interface BoundsWithRange<Range extends BoundsLabel<BoundsConfig<LowerInc, Lower, Int, Upper, UpperInc>>, LowerInc extends LowerInEx, Lower extends number, Int extends NumberSet, Upper extends number, UpperInc extends UpperInEx> extends BoundsConfig<LowerInc, Lower, Int, Upper, UpperInc> { range: Range; } /** * Nice, short, human-readable signature which will show up in the IDE. */ export interface Rebounded<Range extends BoundsLabel<BoundsConfig<LowerInc, Lower, Int, Upper, UpperInc>>, LowerInc extends LowerInEx, Lower extends number, Int extends NumberSet, Upper extends number, UpperInc extends UpperInEx> { [BOUNDS]: BoundsWithRange<Range, LowerInc, Lower, Int, Upper, UpperInc>; } export interface ReboundedFromConfig<LowerInc extends LowerInEx, Lower extends number, Int extends NumberSet, Upper extends number, UpperInc extends UpperInEx> extends Rebounded<BoundsLabel<BoundsConfig<LowerInc, Lower, Int, Upper, UpperInc>>, LowerInc, Lower, Int, Upper, UpperInc> { } export interface ReboundedFromChecked<IsLowerInc extends boolean, Lower extends number, IsInt extends boolean, Upper extends number, IsUpperInc extends boolean> extends Rebounded<BoundsLabel<DefinedFromChecked<IsLowerInc, Lower, IsInt, Upper, IsUpperInc>>, LowerInExFrom<IsLowerInc>, Lower, NumberSetFrom<IsInt>, Upper, UpperInExFrom<IsUpperInc>> { } /** * Brand a number type with a type-generated explanation of its bounds, * so that it can only accept other numbers with the exact same bounds. */ export type BoundedNumber<Bounds> = Bounds extends BoundsConfig<infer LowerInc, infer Lower, infer Int, infer Upper, infer UpperInc> ? (number & ReboundedFromConfig<LowerInc, Lower, Int, Upper, UpperInc>) : Bounds extends CheckedBoundsConfig<infer IsLowerInc extends boolean, infer Lower extends number, infer IsInt extends boolean, infer Upper extends number, infer IsUpperInc extends boolean> ? (number & ReboundedFromChecked<IsLowerInc, Lower, IsInt, Upper, IsUpperInc>) : never; /** * Helper for checking if you're dealing with a {@link BoundedNumber}. */ export type IsBounded<N extends number> = N extends { [BOUNDS]: object; } ? true : false; /** * You could also just cast to `number`, but this provides a nice * explicit and hard-to-miss visual. It is also helpful in downstream * type definitions where you need the bound types to be fungible. */ export type Unbound<N extends number> = IsBounded<N> extends true ? number : N; /** * Extract the value for the lower bound. */ export type LowerBound<N extends number> = N extends { [BOUNDS]: { lower: infer Lower extends number; }; } ? Lower : never; /** * Extract whether the lower bound is inclusive (true) or exclusive (false). */ export type LowerBoundIsInc<N extends number> = N extends { [BOUNDS]: { lowerInc: infer LowerInc extends LowerInEx; }; } ? LowerInc extends LowerInclusive ? true : false : never; /** * Extract the value for the upper bound. */ export type UpperBound<N extends number> = N extends { [BOUNDS]: { upper: infer Upper extends number; }; } ? Upper : never; /** * Extract whether the lower bound is inclusive (true) or exclusive (false). */ export type UpperBoundIsInc<N extends number> = N extends { [BOUNDS]: { upperInc: infer UpperInc extends UpperInEx; }; } ? UpperInc extends UpperInclusive ? true : false : never; /** * Extract whether the number only accepts integers. */ export type IsIntegersOnly<N extends number> = N extends { [BOUNDS]: { int: IntegerSet; }; } ? true : false; /** * Whether the number has defined bounds (neither upper nor lower is infinite). */ export type IsFinite<N extends number> = N extends { [BOUNDS]: { lower: infer Lower extends number; upper: infer Upper extends number; }; } ? Lower extends InfinityBound ? false : Upper extends InfinityBound ? false : true : never; /** * Whether the number has infinite bounds, either upper or lower. */ export type IsInfinite<N extends number> = N extends { [BOUNDS]: { lower: infer Lower extends number; upper: infer Upper extends number; }; } ? Lower extends InfinityBound ? true : Upper extends InfinityBound ? true : false : never; export type OutOfBoundsErrorProvider = (value: unknown, name?: string | undefined) => Error; //# sourceMappingURL=spec.d.ts.map