Skip to content

Commit

Permalink
fix: improve handling of unitless with pow/root functions
Browse files Browse the repository at this point in the history
  • Loading branch information
RebeccaStevens committed Oct 18, 2024
1 parent a7611ba commit ad44cf3
Show file tree
Hide file tree
Showing 4 changed files with 77 additions and 58 deletions.
70 changes: 35 additions & 35 deletions src/math.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import type { UnknownAbstractUnit, UnknownUnit } from "./core";
import type { Exponent, PosExponent } from "./exponents";
import type { Exponent } from "./exponents";
import type { Radian, Unitless } from "./units";
import type { Divide, Inverse, Multiply, Pow, Root } from "./units-operations";

Expand Down Expand Up @@ -133,34 +133,45 @@ export function modSafe(...args: Readonly<[number, number] | [number]>) {
: ((args[0] % args[1]) + args[1]) % args[1];
}

type PowFunction<E extends number, B extends number> = E extends UnknownUnit
? never
: E extends UnknownAbstractUnit
type PowResult<B extends number, E extends number> = E extends Unitless
? PowResult<B, number>
: E extends UnknownUnit
? never
: E extends Exponent
? (b: B) => Pow<B, E>
: E extends 0.5
? (b: B) => Root<B, 2>
: (b: B) => number;
: E extends UnknownAbstractUnit
? never
: E extends Exponent
? Pow<B, E>
: E extends 0.5
? Root<B, 2>
: B extends Unitless
? B
: number;

type PowFunction<B extends number, E extends number> = (b: B) => PowResult<B, E>;

type RootResult<B extends number, E extends number> = E extends Unitless
? RootResult<B, number>
: E extends UnknownUnit
? never
: E extends UnknownAbstractUnit
? never
: E extends Exponent
? Root<B, E>
: E extends 0.5
? Pow<B, 2>
: B extends Unitless
? B
: number;

type RootFunction<B extends number, E extends number> = (b: B) => RootResult<B, E>;

/**
* Raise a number to the power of another.
*
* @category Math
* @returns `base ** exponent`
*/
export function pow<B extends number, E extends number>(
base: B,
exponent: E extends UnknownUnit ? never : E extends UnknownAbstractUnit ? never : E,
): E extends Exponent ? Pow<B, E> : number;

/**
* Put a number to the power of 1/2.
*
* @category Math
* @returns `base ** 0.5`
*/
export function pow<B extends number>(base: B, exponent: 0.5): Root<B, 2>;
export function pow<B extends number, E extends number>(base: B, exponent: E): PowResult<B, E>;

/**
* Put a number to the power of the given value.
Expand All @@ -170,30 +181,19 @@ export function pow<B extends number>(base: B, exponent: 0.5): Root<B, 2>;
*/
export function pow<E extends number>(
exponent: E,
): <B extends number>(base: Parameters<PowFunction<E, B>>[0]) => ReturnType<PowFunction<E, B>>;
): <B extends number>(base: Parameters<PowFunction<B, E>>[0]) => ReturnType<PowFunction<B, E>>;

export function pow(...args: Readonly<[number, number] | [number]>) {
return args.length === 1 ? (b: number) => b ** args[0] : args[0] ** args[1];
}

type RootFunction<E extends number, B extends number> = E extends UnknownUnit
? never
: E extends UnknownAbstractUnit
? never
: E extends PosExponent
? (b: B) => Root<B, E>
: (b: B) => number;

/**
* Take the nth root of a number.
*
* @category Math
* @returns `base ** (1 / exponent)`
*/
export function root<B extends number, E extends number>(
base: B,
exponent: E extends UnknownUnit ? never : E extends UnknownAbstractUnit ? never : E,
): E extends PosExponent ? Root<B, E> : number;
export function root<B extends number, E extends number>(base: B, exponent: E): RootResult<B, E>;

/**
* Take the nth root of a number.
Expand All @@ -203,7 +203,7 @@ export function root<B extends number, E extends number>(
*/
export function root<E extends number>(
exponent: E,
): <B extends number>(base: Parameters<RootFunction<E, B>>[0]) => ReturnType<RootFunction<E, B>>;
): <B extends number>(base: Parameters<RootFunction<B, E>>[0]) => ReturnType<RootFunction<B, E>>;

export function root(...args: Readonly<[number, number] | [number]>) {
return args.length === 1 ? (b: number) => b ** (1 / args[0]) : args[0] ** (1 / args[1]);
Expand Down
36 changes: 18 additions & 18 deletions src/units-operations.ts
Original file line number Diff line number Diff line change
Expand Up @@ -122,45 +122,45 @@ export type DivideUnitSubvalues<A extends UnitSubvalues, B extends UnitSubvalues
*
* @group Unit Functions
*/
export type Pow<T extends number, N extends Exponent> =
T extends Unit<infer Class, infer Meta>
? Unit<FlatternAlias<PowUnitSubvalues<Class, N>>, FlatternAlias<PowUnitSubvalues<Meta, N>>>
: T extends AbstractUnit<infer Class>
? AbstractUnit<FlatternAlias<PowUnitSubvalues<Class, N>>>
: T extends UnitConversionRate<infer Meta>
? UnitConversionRate<FlatternAlias<PowUnitSubvalues<Meta, N>>>
export type Pow<B extends number, E extends Exponent> =
B extends Unit<infer Class, infer Meta>
? Unit<FlatternAlias<PowUnitSubvalues<Class, E>>, FlatternAlias<PowUnitSubvalues<Meta, E>>>
: B extends AbstractUnit<infer Class>
? AbstractUnit<FlatternAlias<PowUnitSubvalues<Class, E>>>
: B extends UnitConversionRate<infer Meta>
? UnitConversionRate<FlatternAlias<PowUnitSubvalues<Meta, E>>>
: number;

/**
* Put each subvalue of a unit to the power of an exponent.
*
* @group Unit Subvalue Functions
*/
export type PowUnitSubvalues<T extends UnitSubvalues, E extends Exponent> = ExcludeUnitZeroSubvalues<{
[S in keyof T]: MultiplyExponents<GetExponent<T, S>, E>;
export type PowUnitSubvalues<B extends UnitSubvalues, E extends Exponent> = ExcludeUnitZeroSubvalues<{
[S in keyof B]: MultiplyExponents<GetExponent<B, S>, E>;
}>;

/**
* Take the nth root of a unit.
*
* @group Unit Functions
*/
export type Root<T extends number, N extends Exponent> =
T extends Unit<infer Class, infer Meta>
? Unit<FlatternAlias<RootUnitSubvalues<Class, N>>, FlatternAlias<RootUnitSubvalues<Meta, N>>>
: T extends AbstractUnit<infer Class>
? AbstractUnit<FlatternAlias<RootUnitSubvalues<Class, N>>>
: T extends UnitConversionRate<infer Meta>
? UnitConversionRate<FlatternAlias<RootUnitSubvalues<Meta, N>>>
export type Root<B extends number, E extends Exponent> =
B extends Unit<infer Class, infer Meta>
? Unit<FlatternAlias<RootUnitSubvalues<Class, E>>, FlatternAlias<RootUnitSubvalues<Meta, E>>>
: B extends AbstractUnit<infer Class>
? AbstractUnit<FlatternAlias<RootUnitSubvalues<Class, E>>>
: B extends UnitConversionRate<infer Meta>
? UnitConversionRate<FlatternAlias<RootUnitSubvalues<Meta, E>>>
: number;

/**
* Take the nth root of each subvalue of a unit.
*
* @group Unit Subvalue Functions
*/
export type RootUnitSubvalues<T extends UnitSubvalues, E extends Exponent> = ExcludeUnitZeroSubvalues<{
[S in keyof T]: DivideExponents<GetExponent<T, S>, E>;
export type RootUnitSubvalues<B extends UnitSubvalues, E extends Exponent> = ExcludeUnitZeroSubvalues<{
[S in keyof B]: DivideExponents<GetExponent<B, S>, E>;
}>;

// Tests
Expand Down
10 changes: 9 additions & 1 deletion tests/math/pow.test-d.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { expectType } from "tsd";

import { type Unit, pow } from "../../src";
import { type Unit, type Unitless, pow } from "../../src";

declare const a: Unit<{ a: 2 }>;

Expand All @@ -10,3 +10,11 @@ expectType<Unit<{ a: 1 }>>(pow(a, 0.5));
expectType<Unit<{ a: 2 }>>(pow(a, 1));
expectType<Unit<{ a: 4 }>>(pow(a, 2));
expectType<Unit<{ a: 6 }>>(pow(a, 3));
expectType<number>(pow(a, 1.2345));

declare const b: Unitless;

expectType<Unitless>(pow(b, 1.2345));
expectType<Unitless>(pow(b, b));

expectType<number>(pow(2, 3));
19 changes: 15 additions & 4 deletions tests/math/root.test-d.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,20 @@
import { expectType } from "tsd";

import { type Unit, root } from "../../src";
import { type Unit, type Unitless, root } from "../../src";

declare const a: Unit<{ a: 6 }>;

expectType<Unit<{ a: 6 }>>(root(1)(a));
expectType<Unit<{ a: 3 }>>(root(2)(a));
expectType<Unit<{ a: 2 }>>(root(3)(a));
expectType<Unit<{ a: -6 }>>(root(a, -1));
expectType<Unit<{}>>(root(a, 0));
expectType<Unit<{ a: 12 }>>(root(a, 0.5));
expectType<Unit<{ a: 6 }>>(root(a, 1));
expectType<Unit<{ a: 3 }>>(root(a, 2));
expectType<Unit<{ a: 2 }>>(root(a, 3));
expectType<number>(root(a, 1.2345));

declare const b: Unitless;

expectType<Unitless>(root(b, 1.2345));
expectType<Unitless>(root(b, b));

expectType<number>(root(2, 3));

0 comments on commit ad44cf3

Please sign in to comment.