primed-model
    TypeScript icon, indicating that this package has built-in type declarations

    1.0.4 • Public • Published

    primed-model

    npm NPM

    Hassle free TypeScript model management

    Dynamically create your instances from JSON objects to take full advantage of OOP. This approach is to be contrasted with factory functions for managing highly structured data

    Note: This project uses reflect-metadata for storing metadata in classes and properties

    Model definition

    • Model <decorator> - To register the classes
    • Primed <decorator> - To mark properties to be dynamically instantiated
    • Base <class> - To extend the models, configure their constructor's typing, and give internal functionality
    @Model
    class Person extends Base<Person> {
        name: string = ''
        middleName: string = ''
        lastName: string = ''
     
        get fullName(){
            return [this.name, this.middleName, this.lastName].join(' ').trim() || 'Empty Name'
        }
     
        @Primed(Person)
        parent!: Person
     
        @Primed('Cat', { array: true })
        cats!: Cat[]
    }
     
    @Model
    class Cat extends Base<Cat>{
        name: string | null = null
        breed: string | null = null
    }

    Note: Cat is being passed to the Primed decorator as a string in the Person class because TS/JS does not allow classes which haven't been defined to be referenced by value

    Example usage

    Initializing with empty constructor

    new Person()

    Output

    {
        name: "",
        middleName: "",
        lastName: "",
        fullName: "Empty Name",
        parent: {
            name: "",
            middleName: "",
            lastName: "",
            fullName: "Empty Name",
            cats: [
                {
                    name: null,
                    breed: null
                }
            ]
        },
        cats:[
            {
                name: null,
                breed: null
            }
        ]
    }

    Passing JSON to constructor

    new Person({
        name: "Alice",
        lastName: "Liddell",
        cats: [
            {
                name: "garfield"
            }
        ],
        parent: {
            name: "Bob",
            cats: [
                {
                    name: "Tom"
                }
            ]
        }
    })

    Output

    {
        name: "Alice",
        middleName: "",
        lastName: "Liddell",
        fullName: "Alice Liddell",
        parent: {
            name: "Bob",
            middleName: "",
            lastName: "",
            fullName: "Bob",
            cats: [
                {
                    name: "Tom",
                    breed: null
                }
            ]
        },
        cats: [
            {
                name: "Garfield",
                breed: null
            }
        ]
    }

    Features

    • Recursively and dynamically create model instances, automatically instantiated by default, unless required: false is specified in the Primed options
    • Specify properties for classes defined after the point of reference passing a string to Primed
    • Pass a factory function to Primed for custom data types
    • Getters are enumerable by default so that they show when iterating over the keys or in the string representation of your class (using JSON.stringify)
    • Provided clone method for copying whole instances

    Custom properties examples

    You can define your own functions that can be passed into the Prime decorator for custom behavior

    Luxon's DateTime

    import { DateTime } from 'luxon'
     
    function PrimedDateTime(value?: string | DateTime): DateTime {
        if(value instanceof DateTime){
            return DateTime.fromJSDate(value.toJSDate()) // Copy the value
        } else if(typeof value === 'string'){
            return DateTime.fromISO(value) // Build from string
        } else {
            return DateTime.local() // Create default value
        }
    }
     
    @Model
    class Foo extends Base<Foo>{
        @Primed(PrimedDateTime)
        someDateTime!: DateTime
    }
    Decimal from decimal.js
    import { Decimal } from 'decimal.js'
     
    function PrimedDecimal(value: number | string | Decimal = 0): Decimal {
        return new Decimal(value)
    }
    JavaScript's Date
    function PrimedDate(value?: string | Date): Date {
        if(typeof value === 'undefined'){
            return new Date() // Create default value
        } else {
            return new Date(value) // Build from string or copy existing
        }
    }
    string id
    function PrimedId(value?: string): string {
        return value ? value : "-1"
    }

    Noteworthy

    • If you're minifying/compressing/uglyfing your JS, you must pass a string to the Model decorator with the name of the class. The function name is being relied uppon for initializing properties that depend on it at runtime

      @Model('Foo')
      class Foo extends Base<Foo>{
          @Primed(Bar)
          bar!: Bar
      }
    • Pass required: false to the Primed options to prevent primed properties from being automatically instantiated

      @Model
      class Foo extends Base<Foo>{
          @Primed(Bar, { required: false })
          bar!: Bar
      }
    • Pass array: true to the Primed options to automatically instantiate arrays

      @Model
      class Foo extends Base<Foo>{
          @Primed(Bar, { array: true })
          bar!: Bar[]
      }
    • If the payload type differs from the class's type and you want those typings, you can pass an second interface (which can be a partial of the class) to the Base class when extending

      interface FooInput{
          someNumber: number,
          someDate: string,
      }
       
      @Model
      class Foo extends Base<Foo, FooInput>{
          someString: string = ''
       
          @Primed(PrimedDecimal)
          someNumber!: Decimal
       
          @Primed(PrimedDateTime)
          someDate!: DateTime
      }
    • Auto initialization will stop after detecting a circular reference

      @Model
      class Alpha extends Base<Alpha> {
          @Primed('Bravo')
          bravo!: Bravo
      }
       
      @Model
      class Bravo extends Base<Bravo> {
          @Primed('Charlie')
          charlie!: Charlie
      }
       
      @Model
      class Charlie extends Base<Charlie> {
          @Primed(Alpha)
          alpha!: Alpha
      }
       
      new Alpha()

      #### Output

      {
          bravo: {
              charlie: {
                  alpha: {
                      bravo: undefined
                  }
              }
          }
      }

    To do

    • Add tests
    • Implement change detection mechanism

    Install

    npm i primed-model

    DownloadsWeekly Downloads

    28

    Version

    1.0.4

    License

    MIT

    Unpacked Size

    22.3 kB

    Total Files

    7

    Last publish

    Collaborators

    • cuzox