Skip to content

Commit

Permalink
fix: improve core unit and prefix logic
Browse files Browse the repository at this point in the history
  • Loading branch information
RebeccaStevens committed Aug 11, 2023
1 parent 673f0b4 commit 9bf4c9f
Show file tree
Hide file tree
Showing 2 changed files with 118 additions and 25 deletions.
4 changes: 2 additions & 2 deletions scripts/generate-files.ts
Original file line number Diff line number Diff line change
Expand Up @@ -64,14 +64,14 @@ function generateExponentsFile() {
}

function generateSiUnitPrefixesFile() {
const imports = `import { type Multiply, type Unit, type UnknownUnit } from "#uom-types";\n\n`;
const imports = `import { type Multiply, type UnknownUnit, type UnitConversionRate } from "#uom-types";\n\n`;
const main = [...exponents.values()]
.map((exponent) => {
const name = scalar10ToName.get(exponent);
if (name === undefined) {
return null;
}
return `/**\n * Binary prefix denoting an order of magnitude of 10^${exponent}.\n *\n * @group Modifiers\n * @category Metric Prefixes\n */\nexport type ${name}<T extends UnknownUnit> = Multiply<T, Unit<{}, { scalar10: ${exponent} }>>;`;
return `/**\n * Binary prefix denoting an order of magnitude of 10^${exponent}.\n *\n * @group Modifiers\n * @category Metric Prefixes\n */\nexport type ${name}<T extends UnknownUnit> = Multiply<T, UnitConversionRate<{ scalar10: ${exponent} }>>;`;
})
.filter(isNotNull)
.join("\n\n");
Expand Down
139 changes: 116 additions & 23 deletions src/core.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
// eslint-disable-next-line import/no-extraneous-dependencies
import { assert, type Extends } from "tsafe";
import { assert, type Equals, type Extends } from "tsafe";

import { type Exponent } from "./exponents";
import { type RemoveNeverValues, type ExcludeUnitZeroSubvalues } from "./utils";
Expand Down Expand Up @@ -47,7 +47,7 @@ export type UnitFrom<
M extends UnknownUnitMeta = UnitMeta<{}>,
> = C extends UnitClass<infer A>
? M extends UnitMeta<infer B>
? Unit<UnitSubvalues extends A ? {} : A, UnitSubvalues extends B ? {} : B>
? Unit<A, B>
: never
: never;

Expand All @@ -68,9 +68,7 @@ export type UnitConversionRate<M extends UnitSubvalues> = number & {
* @group Unit Generators
*/
export type UnitConversionRateFrom<M extends UnknownUnitMeta> =
M extends UnitMeta<infer A>
? UnitConversionRate<UnitSubvalues extends A ? {} : A>
: never;
M extends UnitMeta<infer A> ? UnitConversionRate<A> : never;

/**
* Used to determine what a unit is of. For example, length, volume, mass etc.
Expand Down Expand Up @@ -156,23 +154,118 @@ type UnknownUnitKeyValues = {
export type UnitSubvalues = Record<string, Exponent>;

// Tests
// eslint-disable-next-line functional/no-conditional-statements
if (import.meta.vitest !== undefined) {
assert<Extends<Unit<{}>, UnknownUnit>>();
assert<Extends<AbstractUnit<{}>, UnknownAbstractUnit>>();
assert<Extends<UnitClass<{}>, UnknownUnitClass>>();
assert<Extends<UnitMeta<{}>, UnknownUnitMeta>>();
assert<Extends<UnitKeyValues<{}>, UnknownUnitKeyValues>>();

assert<Extends<Unit<{ a: 1 }>, UnknownUnit>>();
assert<Extends<AbstractUnit<{ a: 1 }>, UnknownAbstractUnit>>();
assert<Extends<UnitClass<{ a: 1 }>, UnknownUnitClass>>();
assert<Extends<UnitMeta<{ a: 1 }>, UnknownUnitMeta>>();
assert<Extends<UnitKeyValues<{ a: 1 }>, UnknownUnitKeyValues>>();

assert<Extends<Unit<UnitSubvalues>, UnknownUnit>>();
assert<Extends<AbstractUnit<UnitSubvalues>, UnknownAbstractUnit>>();
assert<Extends<UnitClass<UnitSubvalues>, UnknownUnitClass>>();
assert<Extends<UnitMeta<UnitSubvalues>, UnknownUnitMeta>>();
assert<Extends<UnitKeyValues<UnitSubvalues>, UnknownUnitKeyValues>>();
const { describe, it } = import.meta.vitest;

describe("Unit", () => {
it("is equivalent to UnitFrom", () => {
assert<Equals<Unit<{}>, UnitFrom<UnitClass<{}>>>>();
assert<Equals<Unit<{ a: 1 }>, UnitFrom<UnitClass<{ a: 1 }>>>>();
assert<Equals<Unit<UnitSubvalues>, UnitFrom<UnitClass<UnitSubvalues>>>>();
});
});

describe("AbstractUnit", () => {
it("a super type of Unit", () => {
assert<Extends<Unit<{}>, AbstractUnit<{}>>>();
assert<Extends<Unit<{ a: 1 }>, AbstractUnit<{ a: 1 }>>>();
assert<Extends<Unit<UnitSubvalues>, AbstractUnit<UnitSubvalues>>>();
assert<
Extends<UnitFrom<UnitClass<{}>>, AbstractUnitFrom<UnitClass<{}>>>
>();
assert<
Extends<
UnitFrom<UnitClass<{ a: 1 }>>,
AbstractUnitFrom<UnitClass<{ a: 1 }>>
>
>();
assert<
Extends<
UnitFrom<UnitClass<UnitSubvalues>>,
AbstractUnitFrom<UnitClass<UnitSubvalues>>
>
>();
});
});

describe("UnitConversionRate", () => {
it("a super type of Unit", () => {
assert<Extends<Unit<{}>, UnitConversionRate<{}>>>();
assert<Extends<Unit<{}, { a: 1 }>, UnitConversionRate<{ a: 1 }>>>();
assert<
Extends<Unit<{}, UnitSubvalues>, UnitConversionRate<UnitSubvalues>>
>();
assert<
Extends<UnitFrom<UnitClass<{}>>, UnitConversionRateFrom<UnitMeta<{}>>>
>();
assert<
Extends<
UnitFrom<UnitClass<{}>, UnitMeta<{ a: 1 }>>,
UnitConversionRateFrom<UnitMeta<{ a: 1 }>>
>
>();
assert<
Extends<
UnitFrom<UnitClass<{}>, UnitMeta<UnitSubvalues>>,
UnitConversionRateFrom<UnitMeta<UnitSubvalues>>
>
>();
});
});

describe("Unknown", () => {
const assignableToConcreteTypes = "is assignable to concrete types";

describe("Unit", () => {
it(assignableToConcreteTypes, () => {
assert<Extends<Unit<{}>, UnknownUnit>>();
assert<Extends<Unit<{ a: 1 }>, UnknownUnit>>();
assert<Extends<Unit<UnitSubvalues>, UnknownUnit>>();
});
});

describe("AbstractUnit", () => {
it(assignableToConcreteTypes, () => {
assert<Extends<AbstractUnit<{}>, UnknownAbstractUnit>>();
assert<Extends<AbstractUnit<{ a: 1 }>, UnknownAbstractUnit>>();
assert<Extends<AbstractUnit<UnitSubvalues>, UnknownAbstractUnit>>();
});
});

describe("UnitConversionRate", () => {
it(assignableToConcreteTypes, () => {
assert<Extends<UnitConversionRate<{}>, UnknownUnitConversionRate>>();
assert<
Extends<UnitConversionRate<{ a: 1 }>, UnknownUnitConversionRate>
>();
assert<
Extends<UnitConversionRate<UnitSubvalues>, UnknownUnitConversionRate>
>();
});
});

describe("UnitClass", () => {
it(assignableToConcreteTypes, () => {
assert<Extends<UnitClass<{}>, UnknownUnitClass>>();
assert<Extends<UnitClass<{ a: 1 }>, UnknownUnitClass>>();
assert<Extends<UnitClass<UnitSubvalues>, UnknownUnitClass>>();
});
});

describe("UnitMeta", () => {
it(assignableToConcreteTypes, () => {
assert<Extends<UnitMeta<{}>, UnknownUnitMeta>>();
assert<Extends<UnitMeta<{ a: 1 }>, UnknownUnitMeta>>();
assert<Extends<UnitMeta<UnitSubvalues>, UnknownUnitMeta>>();
});
});

describe("UnitKeyValues", () => {
it(assignableToConcreteTypes, () => {
assert<Extends<UnitKeyValues<{}>, UnknownUnitKeyValues>>();
assert<Extends<UnitKeyValues<{ a: 1 }>, UnknownUnitKeyValues>>();
assert<Extends<UnitKeyValues<UnitSubvalues>, UnknownUnitKeyValues>>();
});
});
});
}

0 comments on commit 9bf4c9f

Please sign in to comment.