@metamatter/core
TypeScript icon, indicating that this package has built-in type declarations

1.1.9 • Public • Published

Nest Logo

A Type Generation Library for building exportable backend type definition, easily to adopt in different TypeScript based backend development

NPM Version Package License NPM Downloads Language grade: JavaScript Total Alerts

Description

MetaMatter is a library for building runtime type definition generation. It is built with TypeScript and combines the concepts of Meta-Programming, to create type-safe, scalable environment for (mainly) Frontend development.

Philosophy

There are numbers of libraries for building a Node.js server-side applications. However, there is an issue when it comes to communicate between Frontend and Backend no matter which backend language we are using. Type Definitions, would be troublesome when it comes to Agile development style. Frequent updates on APIs makes it hard to develop projects in a type-safe environment.

MetaMatter aims to address this issue, by providing a lightweight, less configuration tools.

Installation

> yarn add @metamatter/core
# or
> npm install @metamatter/core

Usage

There are three decorators at the moment.

  • @Property()
  • @Enum()
  • @Subclass()

@Property()

This is the basic decorator to tell the program to store the decorated field as part of the definition output.

Due to the limitation of Reflection in TypeScript, union types, interface, enum, Promise, Array are not working as expected, the implementation would not be as usual as it be.

Example

import { Property } from '@metamatter/core';

class User {
    @Property()
    username: string;

    @Property({ type: String })
    hobbies: string[];

    @Property({ nullable: true })
    phone?: string;
}

// Will produce the follow output:
const definition = {
    name: 'User',
    type: 'interface',
    definition: '{username: string, hobbies: string[], phone: string | null}',
};

@Enum()

This decorator will work the same way as enum does, but in JavaScript, enum will be translated to a frozen object, which cannot be able to apply any decorators, so we suggest keeping the enum in a class form with static fields.

Due to the limitation of Reflection in TypeScript, union types, interface, enum, Promise, Array are not working as expected, the implementation would not be as usual as it be.

Example

import { Enum } from '@metamatter/core';

@Enum()
class UserRole {
    static readonly ADMIN = 'ADMIN';
    static readonly USER = 'USER';
}

// Will produce the follow output:
const definition = {
    name: 'UserRole',
    type: 'enum',
    definition: '{ADMIN: "ADMIN", USER: "USER"}',
};

@Subclass()

At some point that your application might grow in size, some class might share the same name but in different structure, in that way the program might generate two different interfaces with the same name, this is not what we want. That's why we need @Subclass decorator.

Since nested class cannot apply decorators as well, we need to declare it outside the class and assign it as a static property of another class

Example (Suggested)

import { Subclass, Property } from '@metamatter/core';

@Subclass()
class Wallet {
    @Property()
    balance: number;
}

class User {
    static Wallet = Wallet;

    @Property({ type: User.Wallet })
    wallet: InstanceType<typeof User.Wallet>;
}

Example

import { Subclass, Property } from '@metamatter/core';

@Subclass()
class Wallet {
    @Property()
    balance: number;
}

class User {
    @Property()
    wallet: Wallet;
}

The above examples work the same way, but just in different style.

MetaMatter

This is the main class of the library, it provide two simple functions

  • getPropertyTree
  • generateTypeDefinition

MetaMatter.getPropertyTree

It accepts a class as parameter and it will return a complete Type tree of all of it's properties.

Example

// What a Type look like
interface Type<T> {
    type: string;
    isPrimitive: boolean;
    isArray: boolean;
    isEnum: boolean;
    isNullable: boolean;
    isSubclass: boolean;
    body: T extends object ? (object extends T ? T : { [P in keyof T]: Type<ExcludeArray<T[P]>> }) : null;
    toString: (prefix?: string) => string;
    toDefinition: (prefix?: string) => string;
}
import { MetaMatter, Property } from '@metamatter/core';

class User {
    @Property()
    name: string;

    @Property()
    age: number;
}

const tree = MetaMatter.getPropertyTree(User);

// Output
const tree = {
    name: {
        type: 'string',
        isPrimitive: true,
        isArray: false,
        isEnum: false,
        isNullable: false,
        isSubclass: false,
        body: null,
        toString: () => 'string',
        toDefinition: () => '';
    },
    age: {
        type: 'number',
        isPrimitive: true,
        isArray: false,
        isEnum: false,
        isNullable: false,
        isSubclass: false,
        body: null,
        toString: () => 'number',
        toDefinition: () => '';
    },
}

MetaMatter.generateTypeDefinition

It accepts a class as parameter and it will produce an array of Definition.

Example

// What a Definition look like
interface Definition {
    name: string;
    type: 'interface' | 'enum';
    definition: string;
}
import { MetaMatter, Property, Subclass } from '@metamatter/core';

@Subclass()
class Wallet {
    @Property()
    balance: number;
}

class User {
    @Property()
    name: string;

    @Property()
    age: number;

    @Property({ nullable: true })
    wallet?: Wallet;
}

const definitions = MetaMatter.generateTypeDefinition(User);

// Output
const definitions = [
    {
        name: 'User',
        type: 'interface',
        definition: '{name: string, age: number, wallet: User$Wallet | null}',
    },
    {
        name: 'User$Wallet',
        type: 'interface',
        definition: '{balance: number}',
    },
];

Package Sidebar

Install

npm i @metamatter/core

Weekly Downloads

1

Version

1.1.9

License

MIT

Unpacked Size

61.2 kB

Total Files

55

Last publish

Collaborators

  • jamyth