UNPKG

@fractorysolutions/safe-units

Version:
110 lines (99 loc) 3.85 kB
import { Exponent } from "../exponent"; import { GenericMeasure, NumericOperations } from "./genericMeasure"; import { createMeasureClass } from "./genericMeasureClass"; import { GenericMeasureStatic, getGenericMeasureStaticMethods, } from "./genericMeasureStatic"; import { IsSingleStringLiteral } from "./typeUtils"; import { Unit } from "./unitTypeArithmetic"; import { dimension } from "./unitValueArithmetic"; type DimensionResult< N, D extends string, > = true extends IsSingleStringLiteral<D> ? GenericMeasure<N, { [Dim in D]: "1" }> : never; /** The functions needed to construct a measure of a given numeric type */ interface GenericMeasureFactory<N> { /** The constructor for this generic measure type, useful for doing `instanceof` checks. */ isMeasure(value: any): value is GenericMeasure<N, any>; /** * Creates a new dimension base unit. * @param dim a unique string literal which names this dimension (e.g. "length") * @param symbol the symbol of the base unit of the dimension (e.g. "m") * @returns A measure representing 1 base unit of the dimension (1 m) */ dimension<D extends string>(dim: D, symbol?: string): DimensionResult<N, D>; /** * Creates a dimensionless measure. * @param value the value of the measure * @returns a measure with no dimensions */ dimensionless( value: N, ): GenericMeasure<N, Record<never, Exponent | undefined>>; /** * Creates a measure as a multiple of another measure. * @param value the number of measures * @param quantity the measure to be multiplied * @param symbol an optional unit symbol for this measure * @returns a measure of value number of quantities. */ of<U extends Unit>( value: N, quantity: GenericMeasure<N, U>, symbol?: string, ): GenericMeasure<N, U>; } type GenericMeasureCommon<N> = GenericMeasureFactory<N> & GenericMeasureStatic<N>; type Omit<T, K extends string> = Pick<T, Exclude<keyof T, K>>; /** * A complete measure type for a given numeric type. This consists of: * - Static methods to construct measures (e.g. `Measure.of`) * - Predefined arithmetic static methods (e.g. `Measure.add`) * - User defined static methods (e.g. `Measure.abs`) */ export type GenericMeasureType< N, StaticMethods extends object, > = GenericMeasureCommon<N> & Omit<StaticMethods, keyof GenericMeasureCommon<N>>; /** * Creates a new measure factory for a given numeric type. The numeric type of the measure is inferred from the * parameter. * @param num the set of numeric operations needed to implement a measure for an arbitrary numeric type * @param staticMethods an object containing methods that should be spread into the static definition of a measure, * useful for attaching static math operations to the type. * @returns a factory for constructing measures of the given numeric type * @example * type MyMeasure<U extends Unit> = GenericMeasure<MyNumberType, U>; * const MyMeasure = createMeasureType({ ... }); */ export function createMeasureType<N, S extends object = object>( num: NumericOperations<N>, staticMethods?: S, ): GenericMeasureType<N, S> { const Measure = createMeasureClass(num); const common: GenericMeasureCommon<N> = { ...getGenericMeasureStaticMethods(), isMeasure: (value): value is GenericMeasure<N, any> => value instanceof Measure, dimensionless: (value) => new Measure(value, {}), dimension: <D extends string>(dim: D, symbol?: string) => new Measure(num.one(), dimension(dim, symbol), symbol) as DimensionResult< N, D >, of: <U extends Unit>( value: N, quantity: GenericMeasure<N, U>, symbol?: string, ) => new Measure(num.mult(value, quantity.value), quantity.unit, symbol), }; return { ...((staticMethods || {}) as any), ...common, }; }