@rickosborne/rebound
Version:
Rick Osborne's utilities for working with bounded numbers
204 lines • 8.75 kB
TypeScript
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