# compose-record

> Type-safe utility library for creating nested Immutable Records

Latest version **3.0.0** (published 2020-12-10) · MIT license · 0 weekly downloads

## Install

```sh
npm install compose-record
pnpm add compose-record
yarn add compose-record
bun add compose-record
```

## Health

**Score 25/100 (F)** — status: abandoned.

Positive: has types; no vulnerabilities; high quality score.

Warnings: low downloads; no esm support.

Negative: abandoned; low maintenance score.

## Facts

| | |
|---|---|
| Version | 3.0.0 |
| Published | 2020-12-10 |
| First published | 2018-08-22 |
| Weekly downloads | 0 |
| License | MIT |
| TypeScript types | bundled |
| Module format | CommonJS |
| Dependencies | 1 |
| Unpacked size | 31.3 KB |
| Known vulnerabilities | 0 |
| Install scripts | no |
| GitHub stars | 1 |
| Author | Tim Voronov |
| Maintainers | ziflex |

## Links

- npm: https://www.npmjs.com/package/compose-record
- Repository: https://github.com/ziflex/compose-record
- Homepage: https://ziflex.github.io/compose-record
- Issues: https://github.com/ziflex/compose-record/issues
- npm.io page: https://npm.io/package/compose-record

## Dependencies (1)

- [is-plain-obj](https://npm.io/package/is-plain-obj.md) ^3.0.0

## Recent versions

- 3.0.0 (latest) — 2020-12-10
- 2.0.0 — 2020-06-24
- 1.3.1 — 2018-10-30
- 1.3.0 — 2018-10-29
- 1.2.0 — 2018-09-20
- 1.1.0 — 2018-08-24
- 1.0.0 — 2018-08-22

## README

# compose-record
> Type-safe utility library for creating nested Immutable Records

[![npm version](https://badge.fury.io/js/compose-record.svg)](https://www.npmjs.com/package/compose-record)
[![Build Status](https://github.com/ziflex/compose-record/workflows/build/badge.svg)](https://github.com/ziflex/compose-record/actions)

## Motivation

[Immutable.js](https://facebook.github.io/immutable-js/) is a great library. Unfortunatelly, in v3 it does not support nested Records well, making its usability less pleasant.
The main purpose of this libary is to make our (my) life easier and reduce the amount of boilerplate code by providing a robust and typesafe wrapper around [Immutable.Record](https://facebook.github.io/immutable-js/docs/#/Record) class which initializes all other nested Immutable classes. It works like ``fromJS`` but for ``Record``.

## Quick start

The following example shows a very basic use of ``compose-class`` using nested Records.

````javascript
import { compose } from 'compose-record';

const Address = compose({
    name: "Address",
    properties: {
        street: { type: String },
        apt: { type: Number }
    }
});

const Profile = compose({
    name: "Profile",
    properties: {
        name: { type: String },
        address: { type: Address }
    }
});

const p = new Profile();

console.log(p.toJS()); 
/*
 * {
 *     name: ''
 *     address: {
 *         street: '',
 *         apt: 0
 *     }
 * }
 * /

````

## Default values

All properties support custom default value. By default, these values are defined by their types.   

````javascript
import { compose } from 'compose-record';

const Address = compose({
    name: "Address",
    properties: {
        street: { type: String },
        apt: { type: Number }
    }
});

const Profile = compose({
    name: "Profile",
    properties: {
        name: {
            type: String,
            defaultValue: 'unknown'
        },
        address: {
            type: Address,
            defaultValue: {
                street: 'unknown',
                apt: -1
            }
        }
    }
});

const p = new Profile();

console.log(p.toJS());

/*
 * {
 *     name: 'unknown',
 *     address: {
 *         street: 'unknown',
 *         apt: -1
 *     }
 * }
 */
````

## Extension

``compose-record`` allows you to extend your Records with other(s).

````javascript
import { compose } from 'compose-record';

const Entity = compose({
    name: "Entity",
    properties: {
        id: { type: Number }
    }
});

const User = compose({
    name: "User",
    extends: Entity,
    properties: {
        name: { type: String }
    }
});

const u = new User();

console.log(p.toJS());

/*
 * {
 *     id: 0,
 *     name: ''
 * }
 */

````

## Item types

That all works fine with Record and primitive types. But what if we need to have a List or a Map with nested Records? ``compose-record`` has a special option for these: ``items``. 
``items`` is a nested type descriptor that informs ``compose-record`` how to wrap the underlying values.
It's optional, by default ``compose-record`` will use a value as it is.

````javascript
import { compose } from 'compose-record';
import { List } from 'immutable';

const User = compose({
    name: 'User',
    properties: {
        name: { type: String }
    }
});

const Group = compose({
    name: 'Group',
    properties: {
        users: { 
            type: List,
            items: {
                type: User
            }
        }
    }
});

const u = new Group({
    users: [
        { name: 'Mike Wazowski' },
        { name: 'James P. Sullivan' }
    ]
});

console.log(p.toJS());

/*
 *    {
 *        users: [
 *            { name: 'Mike Wazowski' },
 *            { name: 'James P. Sullivan' }
 *        ]
 *     }
 */

````

You can build even more complex scenarios:

````javascript
import { compose } from 'compose-record';
import { List, Map } from 'immutable';

const User = compose({
    name: 'User',
    properties: {
        name: { type: String }
    }
});

const Group = compose({
    name: 'Group',
    properties: {
        users: { 
            type: Map,
            items: {
                type: List,
                items: {
                    type: User
                }
            }
        }
    }
});

const u = new Group({
    users: {
        monsters: [
            { name: 'Mike Wazowski' },
            { name: 'James P. Sullivan' }
        ],
        humans: [
            { name: 'Boo' }
        ]
    }
});

console.log(p.toJS());

/*
 * {
 *     users: {
 *         monsters: [
 *             { name: 'Mike Wazowski' },
 *             { name: 'James P. Sullivan' }
 *         ],
 *      humans: [
 *          { name: 'Boo' }
 *      ]
 *   }
 * }
 */

````

**Note:** There is one caveat: It works only for constructors. All further inserts require you to pass an instance of a nested Record. This is a limiation of ```compose-record``` which might be solved in the future.

Current work around is to get a type descriptor and use ``Record.createPropertyInstance()`` method:

```javascript
import { compose } from 'compose-record';
import { List, Map } from 'immutable';

const User = compose({
    name: 'User',
    properties: {
        name: {
            type: String,
        },
    },
});

const Group = compose({
    name: 'Group',
    properties: {
        users: {
            type: List,
            items: {
                type: User,
            },
        },
    },
});

const desc = Group.getPropertyDescriptors();
const u = Group.createPropertyInstance<User>(desc.users.items, {
    name: 'Mike Wazowski',
});

const g = new Group();
console.log(g.users.push(u).toJS());

/*
 *    {
 *        users: [
 *            { name: 'Mike Wazowski' },
 *        ]
 *     }
 */
```

## Enum and custom types

Since version 1.3, it is possible to create a factory function type that is executed as a regular function in order to get a custom value (usually mixed).

Here is an example:

  ```typescript
    import { Immutable, compose } from 'compose-record';

    enum Role {
        User,
        Admin,
        Owner,
    }

    interface User extends Immutable {
        username: string;
        role: Role;
    }

    const UserRecord = compose<User>({
        name: 'User',
        properties: {
            username: {
                type: String,
            },
            role: {
                type: compose.factory<Role, string>((value?: string) => {
                    let result = Role.User;

                    switch (value) {
                    case 'user':
                        result = Role.User;
                        break;
                    case 'admin':
                        result = Role.Admin;
                        break;
                    case 'owner':
                        result = Role.Owner;
                        break;
                    }

                    return result;
                }),
            },
        },
    });
```

---
_Source: https://npm.io/package/compose-record · Machine-readable twin of the npm.io package page. Health data is recomputed on every publish._
