Skip to content

Collection of custom data types with a consistent API

License

Notifications You must be signed in to change notification settings

datatypesjs/datatypes

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

5 Commits
 
 
 
 
 
 

Repository files navigation

Datatypes

Collection of custom data-types with a consistent API.

Modules

Geometry/Graphics

  • circle-sector - Class for Circle sector with SVG support.
  • face - Class for 2-faces to use as facets of polyhedrons and polygon meshes.
  • matrix - Class for 4x4 row-major matrices for transformations in 3D.
  • point - Class for points in 3D space.
  • vector - Class for 3D vectors.

Time

  • duration - Full ISO 8601 compatible duration class.
  • interval - ISO 8601 based interval class.
  • moment - ISO 8601 based time and date class.

Data structures

Principles

  • One class per file
  • Class name must be the same as file name

Each datatype is implemented by an ES2015 class.

export default class Color {
  
}

Each class must be instantiated with the new keyword.

const color = new Color()

The constructor always expects exactly one object as the argument or no argument at all.

Example:

const color = new Color({red: 250, green: 35, blue: 129})

All object keys must be assigned to the object instance (e.g. through Object.assign(this, optionsObject)). This allows the user of the class to easily assign custom object properties in addition to the designated ones.

To provide other constructor methods use the from<datatype> pattern.

Where <datatype> must contain the native JavaScript data type (e.g. String, Number, Array, Object, Date, …) if it's not a custom datatype.

Examples:

const color = Color.fromHexString('#FA2381')
const color = Color.fromRgbArray([250, 35, 129])
const color = Color.from(car)

.from() is a special constructor which accesses the relevant Data through duck typing. So as long as an object has the properties red, green and blue it can be be used with the .from() constructor. (This is derived from the native Array.from)

Each public computed property must have a setter and a getter. Always using getters (e.g. hsl instead of toHsl) avoids premature optimization and a ensures a consistent API. It also hides the implementation detail if a property is static or is dynamically created.

export default class Color {
  
  get red () { return this._red }
  set red (value) {
    this._red = value
    // To other related computation
    return this
  }
  
}

Additionally to the setter there must be a .set<property> method to enable chaining.

Example:

const color = new Color()
  .setRed(250)
  .setGreen(35)
  .setBlue(129)

Evaluate getters lazily. (E.g. don't recalculate the internal HSL value on setting red but rather when the new HSL value is requested) Furthermore a .computeHsl() method can be provided to let the user decide when to recalculate values.

// When computation time is abundantly available
const hslObject = new Color()
  .setRed(250)
  .computeHsl()

// Later, when computation time is limited
doSomething(hslObject.hsl)

All methods must accept one argument at most. For more arguments an object must be used.

  • Ensures backwards compatible extensibility
  • Increases readability

Example:

const color = new Color({red: 250, green: 35, blue: 129})

instead of

const color = new Color(250, 35, 129)

Mandatory instance methods:

  • toJSON - Must return a plain JavaScript object which can be JSON stringified. For example color.toJSON() could yield {red: 250, green: 35, blue: 129} .toJSON() is an alias for .object

  • toString - Must return a string representation of the object. For example color.toString() could yield 'rgb(250, 35, 129)' .toString() is an alias for .string


Mandatory class methods:

  • fromObject - Create instance from plain JavaScript object.
  • schema - Returns JSON schema for the fromObject argument.
  • validate - Validates an object against the JSON schema.

Private methods must start with an underscore _


Datatypes classes shall be preferred over custom implementations.


Attention: color instanceof Color is anti-pattern in most cases!

Instead of if (color instanceof Color) return color.red use if (color.hasOwnProperty('red')) return color.red


Use "synchronous usage of asynchronous code" pattern for asynchronous code.

No inheritance

class Person extends Mammal extends Animal extends Organism extends 

Although correct, it's not really helpful, consumes a lot of memory and method lookup hurts performance.

Better: Explicitly set a class as prototype if methods and properties of a possible base class are needed.

Normalized Object Schema

The root element must be an object:

users:
  - id: 1
  - id: 2

instead of

- id: 1
- id: 2

Avoid singular terms.

Instead of

email: john@work.com

use

emails:
  - 
  - 

Objects may not be misused as arrays

Instead of similar keys

privateEmail: john@home.com
workEmail: john@work.com

use arrays

emails:
  - value: john@home.com
    type: private
  - value: john@work.com
    type: work

Avoid primitives in arrays:

Instead of

emails:
  - john@home.com
  - john@work.com

use

emails:
  - value: john@home.com
    type: private
  - value: john@work.com
    type: work

Make sure the primitive value can not be further specialized:

{
  email: 'john.doe@example.com'
}

should be

{
  email: new Email('john.doe@example.com')
}

This, on the other hand, is OK:

{
  id: 2342
}

Usage

Don't use primitives when you need to perform computation on that data. E.g. You can use '2003-09-23' to store a birthday, but you should use new Day('2003-09-23') if you need to calculate the age from it.

About

Collection of custom data types with a consistent API

Resources

License

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published