Skip to content

Commit

Permalink
fix: critical type bugs
Browse files Browse the repository at this point in the history
  • Loading branch information
RebeccaStevens committed Jul 7, 2023
1 parent 409b71d commit 46ef7b9
Show file tree
Hide file tree
Showing 3 changed files with 187 additions and 131 deletions.
56 changes: 33 additions & 23 deletions src/base/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,10 +7,16 @@ import {
} from "./core";

export type BrandUnit<T extends Record<string, UnitValue>> = {
__exactKeys: keyof T;
__uom_types: {
[U in keyof T]: BrandUnitConfigValue<T[U]>;
};
__exactKeys: keyof ExcludeZeroExponentsKeys<T>;
__uom_types: RemoveNever<{
[U in keyof T]: U extends keyof ExcludeZeroExponentsKeys<T>
? BrandUnitConfigValue<T[U]>
: never;
}>;
};

type RemoveNever<T> = {
[K in keyof T as [T[K]] extends [never] ? never : K]: T[K];
};

type BrandUnitConfigValue<T extends UnitValue> = ExcludeZeroExponents<T> & {
Expand All @@ -19,6 +25,10 @@ type BrandUnitConfigValue<T extends UnitValue> = ExcludeZeroExponents<T> & {

type GetUnitConfig<T> = T extends UnitFull<infer C> ? C : never;

type ExcludeZeroExponentsKeys<T extends Record<string, UnitValue>> = {
[K in keyof T as T[K] extends { exponent: 0 } ? never : K]: T[K];
};

type ExcludeZeroExponents<T extends UnitValue> = {
[K in keyof T as T[K] extends 0 ? never : K]: T[K];
};
Expand All @@ -37,11 +47,9 @@ type KeysOfKeyOfTwoObjects<A, B, K> = K extends keyof A
? keyof B[K]
: never;

export type Inverse<T extends number> = T extends Exponent
? NegativeExponent<T>
: T extends UnknownUnit
export type Inverse<T extends number> = T extends UnknownUnit
? InverseUnit<T>
: never;
: number;

type NegativeExponent<T extends Exponent> = T extends -6
? 6
Expand Down Expand Up @@ -84,21 +92,19 @@ type InverseUnitCore<T extends UnknownUnit> = {
};
};

export type Multiply<A extends number, B extends number> = A extends Exponent
? B extends Exponent
? SumExponents<A, B>
: never
: A extends UnknownUnit
export type Multiply<A extends number, B extends number> = A extends UnknownUnit
? B extends UnknownUnit
? MultiplyUnits<A, B>
: never
: never;
: A
: B extends UnknownUnit
? B
: number;

type MultiplyUnits<
A extends UnknownUnit,
B extends UnknownUnit,
> = MultiplyUnitsCore<A, B> extends Record<string, UnitValue>
? Unit<MultiplyUnitsCore<A, B>>
? Unit<FlatternAlias<MultiplyUnitsCore<A, B>>>
: never;

type MultiplyUnitsCore<A extends UnknownUnit, B extends UnknownUnit> = {
Expand All @@ -122,15 +128,13 @@ type MultiplyUnitsCore<A extends UnknownUnit, B extends UnknownUnit> = {
};
};

export type Divide<A extends number, B extends number> = A extends Exponent
? B extends Exponent
? SubExponents<A, B>
: never
: A extends UnknownUnit
export type Divide<A extends number, B extends number> = A extends UnknownUnit
? B extends UnknownUnit
? DivideUnits<A, B>
: never
: never;
: A
: B extends UnknownUnit
? Inverse<B>
: number;

type DivideUnits<A extends UnknownUnit, B extends UnknownUnit> = MultiplyUnits<
A,
Expand Down Expand Up @@ -397,3 +401,9 @@ type SumExponents<A extends Exponent, B extends Exponent> = A extends -6
? A
: never
: never;

/**
* Flatten a complex type such as a union or intersection of objects into a
* single object.
*/
export type FlatternAlias<T> = { [P in keyof T]: T[P] } & {};
108 changes: 62 additions & 46 deletions src/functions-ho/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,116 +4,132 @@ import {
type Inverse,
type UnknownUnit,
} from "uom-types";
import { type Decimal } from "uom-types/si-units";

type OperationResult<T extends number> = T extends UnknownUnit ? T : number;
type OperationIO<T extends number> = T extends UnknownUnit ? T : number;

/**
* Add a value by the given value.
*/
export function add<T extends number>(a: T): (b: T) => OperationResult<T> {
return (b) => (a + b) as OperationResult<T>;
export function add<T extends number>(
a: OperationIO<T>,
): (b: OperationIO<T>) => OperationIO<T> {
return (b) => (b + a) as OperationIO<T>;
}

/**
* Subtract one value from the given value.
*/
export function sub<T extends number>(a: T): (b: T) => OperationResult<T> {
return (b) => (b - a) as OperationResult<T>;
export function sub<T extends number>(
a: OperationIO<T>,
): (b: OperationIO<T>) => OperationIO<T> {
return (b) => (b - a) as OperationIO<T>;
}

/**
* Multiple a value by the given value.
*/
export function mul<T extends number>(a: T): (b: T) => OperationResult<T> {
return (b) => (a * b) as OperationResult<T>;
export function mul<A extends number>(
a: OperationIO<A>,
): <B extends number>(b: OperationIO<B>) => OperationIO<Multiply<B, A>> {
// eslint-disable-next-line @typescript-eslint/no-explicit-any -- Casting to actual type fails for some reason.
return (b) => (b * a) as any;
}

/**
* Divide one value by the given value.
*/
export function div<T extends number>(a: T): (b: T) => OperationResult<T> {
return (b) => (b / a) as OperationResult<T>;
export function div<A extends number>(
a: OperationIO<A>,
): <B extends number>(b: OperationIO<B>) => OperationIO<Divide<B, A>> {
// eslint-disable-next-line @typescript-eslint/no-explicit-any -- Casting to actual type fails for some reason.
return (b) => (b / a) as any;
}

/**
* Modulo operator.
*/
export function mod<T extends number>(a: T): (b: T) => OperationResult<T> {
return (b) => (b % a) as OperationResult<T>;
export function mod<T extends number>(
a: OperationIO<T>,
): (b: OperationIO<T>) => OperationIO<T> {
return (b) => (b % a) as OperationIO<T>;
}

/**
* Perform mathematic modular arithmetic.
*/
export function modSafe<T extends number>(a: T): (b: T) => OperationResult<T> {
return (b) => (((b % a) + a) % a) as OperationResult<T>;
export function modSafe<T extends number>(
a: OperationIO<T>,
): (b: OperationIO<T>) => OperationIO<T> {
return (b) => (((b % a) + a) % a) as OperationIO<T>;
}

type PowFunction<A extends number, B extends number> = A extends -1
? (b: B) => OperationResult<Inverse<B>>
: A extends 0
? (b: B) => 1
: A extends 0.5
? (b: B) => OperationResult<Divide<B, 2>>
: A extends 1
? (b: B) => B
: A extends 2
? (b: B) => OperationResult<Multiply<B, B>>
: A extends 3
? (b: B) => OperationResult<Multiply<B, Multiply<B, B>>>
: A extends 4
? (b: B) => OperationResult<Multiply<B, Multiply<B, Multiply<B, B>>>>
: (b: B) => OperationResult<number>;
type PowFunction<E extends number, B extends number> = E extends -1
? (b: OperationIO<B>) => OperationIO<Inverse<B>>
: E extends 0
? (b: OperationIO<B>) => Decimal | 1
: E extends 0.5
? (b: OperationIO<B>) => OperationIO<Divide<B, 2>>
: E extends 1
? (b: OperationIO<B>) => OperationIO<B>
: E extends 2
? (b: OperationIO<B>) => OperationIO<Multiply<B, B>>
: E extends 3
? (b: OperationIO<B>) => OperationIO<Multiply<B, Multiply<B, B>>>
: E extends 4
? (b: OperationIO<B>) => OperationIO<Multiply<B, Multiply<B, Multiply<B, B>>>>
: (b: OperationIO<B>) => OperationIO<number>;

/**
* Put a number to the power of the given value.
*/
export function pow<A extends number, B extends number>(
a: A,
): PowFunction<A, B> {
return ((b: B) => b ** a) as PowFunction<A, B>;
}

/**
* Take the square root of the given value.
*/
export function sqrt<T extends number>(): (
value: T,
) => OperationResult<Divide<T, 2>> {
return pow(0.5);
export function pow<E extends number, B extends number>(
exponent: E extends UnknownUnit ? never : E,
): PowFunction<E, B> {
return ((base: OperationIO<B>) => base ** exponent) as PowFunction<E, B>;
}

/**
* Equal: Compare if a value is equal to the given value.
*/
export function eq<A extends number>(a: A): (b: A) => boolean {
export function eq<T extends number>(
a: OperationIO<T>,
): (b: OperationIO<T>) => boolean {
return (b) => b === a;
}

/**
* Greater Than: Compare if a value is greater than the given value.
*/
export function gt<A extends number>(a: A): (b: A) => boolean {
export function gt<T extends number>(
a: OperationIO<T>,
): (b: OperationIO<T>) => boolean {
return (b) => b > a;
}

/**
* Greater Than or Equal: Compare if a value is greater than or equal to the given value.
*/
export function gte<A extends number>(a: A): (b: A) => boolean {
export function gte<T extends number>(
a: OperationIO<T>,
): (b: OperationIO<T>) => boolean {
return (b) => b >= a;
}

/**
* Less Than: Compare if a value is less than the given value.
*/
export function lt<A extends number>(a: A): (b: A) => boolean {
export function lt<T extends number>(
a: OperationIO<T>,
): (b: OperationIO<T>) => boolean {
return (b) => b < a;
}

/**
* Less Than or Equal: Compare if a value is less than or equal to the given value.
*/
export function lte<A extends number>(a: A): (b: A) => boolean {
export function lte<T extends number>(
a: OperationIO<T>,
): (b: OperationIO<T>) => boolean {
return (b) => b <= a;
}

0 comments on commit 46ef7b9

Please sign in to comment.