@fractorysolutions/safe-units
Version:
Type-safe TypeScript units of measure
100 lines (81 loc) • 3.08 kB
text/typescript
import {
AddendOf,
AddExponents,
DivideExponents,
Exponent,
MultiplicandOf,
MultiplyExponents,
PositiveExponent,
ProductOf,
SubtractExponents,
SubtrahendOf,
} from "../exponent";
export type Unit = {
[dimension: string]: Exponent | undefined;
};
export type UnitWithSymbols<U extends Unit = Unit> = {
[D in keyof U]+?: [string, NonNullable<U[D]>];
};
export type SymbolAndExponent = [string, Exponent];
// Multiplication
/** Returns the product of two units. This is the sum of two dimension vectors. */
export type MultiplyUnits<L extends Unit, R extends Unit> = {
[Dim in keyof L | keyof R]: AddExponents<
GetExponent<L, Dim>,
GetExponent<R, Dim>
>;
};
/** A type that is assignable from all units that can be multiplied by U without producing an error. */
export type MultiplicandUnit<U extends Unit> = Partial<{
[D in keyof U]: AddendOf<CleanExponent<U[D]>>;
}> &
Unit;
// Division
/** Returns the quotient of two units. This is the difference of two dimension vectors. */
export type DivideUnits<L extends Unit, R extends DivisorUnit<L>> = {
[Dim in keyof L | keyof R]: SubtractExponents<
GetExponent<L, Dim>,
GetExponent<R, Dim>
>;
};
/** A type that is assignable from all units that U can be divided by without producing an error. */
export type DivisorUnit<U extends Unit> =
| U // unit can always be divided by itself, but typescript doesn't quite figure that out
| (Partial<{ [D in keyof U]: SubtrahendOf<CleanExponent<U[D]>> }> & Unit);
// Exponentiation
/** Returns the unit raised to a power. This is the scalar multiple of the dimension vector. */
export type ExponentiateUnit<U extends Unit, N extends Exponent> = "0" extends N
? Record<never, Exponent | undefined>
: { [Dim in keyof U]: MultiplyExponents<GetExponent<U, Dim>, N> };
/** Returns the union of exponents to which a given unit is allowed to be raised. */
export type AllowedExponents<U extends Unit> =
| Exclude<Exponent, NonAllowedExponents<U>>
| "-1"
| "0"
| "1";
/** Returns the union of exponents that raising and exponent to would produce an error. */
type NonAllowedExponents<U extends Unit> = {
[Dim in keyof U]: undefined extends U[Dim]
? never
: Exclude<Exponent, MultiplicandOf<NonNullable<U[Dim]>>>;
}[keyof U];
// Roots
/** Returns the nth root of a unit. This is the inverse scalar multiple of the dimension vector. */
export type NthRootUnit<
U extends RadicandUnit<N>,
N extends PositiveExponent,
> = 1 extends N
? U
: { [Dim in keyof U]: DivideExponents<GetExponent<U, Dim>, N> };
/** A type that is assignable from all units whose Nth root does not produce an error. */
export type RadicandUnit<N extends PositiveExponent> = {
[dimension: string]: ProductOf<N> | undefined;
};
// Utility types
/** Get the exponent at a given dimension of a unit, or 0 if that dimension is undefined */
type GetExponent<U extends Unit, D> = D extends keyof U
? NonNullable<U[D]>
: "0";
type CleanExponent<E extends undefined | Exponent> = undefined extends E
? "0"
: NonNullable<E>;