Skip to content

Commit

Permalink
fix: types static methods and members for Unit class (#3230)
Browse files Browse the repository at this point in the history
* fix: types static method for Unit class

Changes unit interface to declare class to enable the adding of static methods.

* refactor: change to not using declare class

* fix: adds more unit static methods and updates tests

* chore: moves test from wrong location

* fix: adds additional static methods and updates jsDocs

---------

Co-authored-by: Jos de Jong <wjosdejong@gmail.com>
  • Loading branch information
orelbn and josdejong authored Jul 11, 2024
1 parent 975d1ba commit f5a5463
Show file tree
Hide file tree
Showing 3 changed files with 123 additions and 23 deletions.
43 changes: 26 additions & 17 deletions src/type/unit/Unit.js
Original file line number Diff line number Diff line change
Expand Up @@ -466,7 +466,7 @@ export const createUnitClass = /* #__PURE__ */ factory(name, dependencies, ({
* Return the type of the value of this unit
*
* @memberof Unit
* @ return {string} type of the value of the unit
* @return {string} type of the value of the unit
*/
Unit.prototype.valueType = function () {
return typeOf(this.value)
Expand All @@ -476,6 +476,7 @@ export const createUnitClass = /* #__PURE__ */ factory(name, dependencies, ({
* Return whether the unit is derived (such as m/s, or cm^2, but not N)
* @memberof Unit
* @return {boolean} True if the unit is derived
* @private
*/
Unit.prototype._isDerived = function () {
if (this.units.length === 0) {
Expand Down Expand Up @@ -585,7 +586,7 @@ export const createUnitClass = /* #__PURE__ */ factory(name, dependencies, ({
* check if this unit has given base unit
* If this unit is a derived unit, this will ALWAYS return false, since by definition base units are not derived.
* @memberof Unit
* @param {BASE_UNITS | string | undefined} base
* @param {BASE_UNIT | string | undefined} base
*/
Unit.prototype.hasBase = function (base) {
if (typeof (base) === 'string') {
Expand Down Expand Up @@ -2918,6 +2919,7 @@ export const createUnitClass = /* #__PURE__ */ factory(name, dependencies, ({

/**
* Set a unit system for formatting derived units.
* @memberof Unit
* @param {string} [name] The name of the unit system.
*/
Unit.setUnitSystem = function (name) {
Expand All @@ -2930,6 +2932,7 @@ export const createUnitClass = /* #__PURE__ */ factory(name, dependencies, ({

/**
* Return the current unit system.
* @memberof Unit
* @return {string} The current unit system.
*/
Unit.getUnitSystem = function () {
Expand Down Expand Up @@ -3023,7 +3026,9 @@ export const createUnitClass = /* #__PURE__ */ factory(name, dependencies, ({
/**
* Checks if a character is a valid latin letter (upper or lower case).
* Note that this function can be overridden, for example to allow support of other alphabets.
* @memberof Unit
* @param {string} c Tested character
* @return {boolean} true if the character is a latin letter
*/
Unit.isValidAlpha = function isValidAlpha (c) {
return /^[a-zA-Z]$/.test(c)
Expand All @@ -3043,20 +3048,24 @@ export const createUnitClass = /* #__PURE__ */ factory(name, dependencies, ({
/**
* Wrapper around createUnitSingle.
* Example:
* createUnit({
* foo: { },
* bar: {
* definition: 'kg/foo',
* aliases: ['ba', 'barr', 'bars'],
* offset: 200
* },
* baz: '4 bar'
* },
* {
* override: true
* })
* createUnit( {
* foo: {
* prefixes: 'long',
* baseName: 'essence-of-foo'
* },
* bar: '40 foo',
* baz: {
* definition: '1 bar/hour',
* prefixes: 'long'
* }
* },
* {
* override: true
* })
* @memberof Unit
* @param {object} obj Object map. Each key becomes a unit which is defined by its value.
* @param {object} options
* @return {Unit} the last created unit
*/
Unit.createUnit = function (obj, options) {
if (typeof (obj) !== 'object') {
Expand Down Expand Up @@ -3091,13 +3100,13 @@ export const createUnitClass = /* #__PURE__ */ factory(name, dependencies, ({
* Create a user-defined unit and register it with the Unit type.
* Example:
* createUnitSingle('knot', '0.514444444 m/s')
* createUnitSingle('acre', new Unit(43560, 'ft^2'))
*
* @memberof Unit
* @param {string} name The name of the new unit. Must be unique. Example: 'knot'
* @param {string, Unit, Object} definition Definition of the unit in terms
* @param {string | Unit | object} definition Definition of the unit in terms
* of existing units. For example, '0.514444444 m / s'. Can be a Unit, a string,
* or an Object. If an Object, may have the following properties:
* - definition {string|Unit} The definition of this unit.
* - definition {string | Unit} The definition of this unit.
* - prefixes {string} "none", "short", "long", "binary_short", or "binary_long".
* The default is "none".
* - aliases {Array} Array of strings. Example: ['knots', 'kt', 'kts']
Expand Down
54 changes: 54 additions & 0 deletions test/typescript-tests/testTypes.ts
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,7 @@ import {
SymbolNode,
MathNodeCommon,
Unit,
UnitPrefix,
Node,
isSymbolNode,
MathScalarType
Expand Down Expand Up @@ -1609,6 +1610,59 @@ Units examples
math.unit('1 m').splitUnit(['ft', 'in'])
}

/**
* Unit static methods and members
*/
{
expectTypeOf(new Unit(15, 'cm')).toMatchTypeOf<Unit>()

const prefixes = Unit.PREFIXES
assert.ok(Object.keys(prefixes).length > 0)
expectTypeOf(Unit.PREFIXES).toMatchTypeOf<Record<string, UnitPrefix>>()

const baseDimensions = Unit.BASE_DIMENSIONS
assert.ok(baseDimensions.length > 0)
expectTypeOf(Unit.BASE_DIMENSIONS).toMatchTypeOf<string[]>()

const baseUnits = Unit.BASE_UNITS
assert.ok(Object.keys(baseUnits).length > 0)

const units = Unit.UNITS
assert.ok(Object.keys(units).length > 0)

Unit.createUnit(
{
foo: {
prefixes: 'long',
baseName: 'essence-of-foo'
},
bar: '40 foo',
baz: {
definition: '1 bar/hour',
prefixes: 'long'
}
},
{
override: true
}
)

Unit.createUnitSingle('knot', '0.514444444 m/s')

const unitSystems = Unit.UNIT_SYSTEMS
assert.ok(Object.keys(unitSystems).length > 0)

Unit.setUnitSystem('si')
assert.strictEqual(Unit.getUnitSystem(), 'si')

expectTypeOf(Unit.isValuelessUnit('cm')).toMatchTypeOf<boolean>()
expectTypeOf(Unit.parse('5cm')).toMatchTypeOf<Unit>()
expectTypeOf(
Unit.fromJSON({ value: 5.2, unit: 'inch' })
).toMatchTypeOf<Unit>()
expectTypeOf(Unit.isValidAlpha('cm')).toMatchTypeOf<boolean>()
}

/**
* Example of custom fallback for onUndefinedSymbol & onUndefinedFunction
*/
Expand Down
49 changes: 43 additions & 6 deletions types/index.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -508,6 +508,7 @@ export interface MathJsInstance extends MathJsFactory {
RelationalNode: RelationalNodeCtor
SymbolNode: SymbolNodeCtor

Unit: UnitCtor
Matrix: MatrixCtor

/**
Expand Down Expand Up @@ -3989,15 +3990,17 @@ export interface MathJSON {
fixPrefix?: boolean
}

export interface BaseUnit {
dimensions: number[]
key: string
}

export interface UnitComponent {
power: number
prefix: string
unit: {
name: string
base: {
dimensions: number[]
key: string
}
base: BaseUnit
prefixes: Record<string, UnitPrefix>
value: number
offset: number
Expand All @@ -4014,8 +4017,7 @@ export interface UnitPrefix {
export interface Unit {
valueOf(): string
clone(): Unit
// eslint-disable-next-line @typescript-eslint/no-explicit-any
hasBase(base: any): boolean
hasBase(base: BaseUnit | string | undefined): boolean
equalBase(unit: Unit): boolean
equals(unit: Unit): boolean
multiply(unit: Unit): Unit
Expand All @@ -4040,6 +4042,40 @@ export interface Unit {
skipAutomaticSimplification: true
}

export type UnitSystemName = 'si' | 'cgs' | 'us' | 'auto'

export interface UnitStatic {
PREFIXES: Record<string, UnitPrefix>
BASE_DIMENSIONS: string[]
BASE_UNITS: Record<string, BaseUnit>
UNIT_SYSTEMS: Record<
UnitSystemName,
Record<string, { unit: Unit; prefix: UnitPrefix }>
>
UNITS: Record<string, Unit>
parse(str: string): Unit
isValuelessUnit(name: string): boolean
fromJSON(json: MathJSON): Unit
isValidAlpha(c: string): boolean
createUnit(
obj: Record<string, string | Unit | UnitDefinition>,
options?: { override: boolean }
): Unit
createUnitSingle(
name: string,
definition: string | Unit | UnitDefinition
): Unit
getUnitSystem(): UnitSystemName
setUnitSystem(name: UnitSystemName): void
}

export interface UnitCtor extends UnitStatic {
new (
value: number | BigNumber | Fraction | Complex | boolean,
name: string
): Unit
}

export interface CreateUnitOptions {
prefixes?: 'none' | 'short' | 'long' | 'binary_short' | 'binary_long'
aliases?: string[]
Expand Down Expand Up @@ -6853,6 +6889,7 @@ export const {
RelationalNode,
SymbolNode,
Matrix,
Unit,

uninitialized,
version,
Expand Down

0 comments on commit f5a5463

Please sign in to comment.