npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2024 – Pkg Stats / Ryan Hefner

auto-mapping

v1.3.4

Published

map and convert object in typescript

Downloads

152

Readme

auto-mapping

Map and convert objects automatically in typescript.

Features

  • Map object to an instance of a class by annotation;
  • Map object to another object which only has the mapped property;
  • Convert property types automatically;
  • Custom conversion;
  • Multiple data source mappings;
  • Map extensions;

Installation

$ npm install --save auto-mapping

Getting started

Please import the reflect-metadata module in the project entry file before use.

To enable experimental support for decorators, you must enable the experimentalDecorators compiler option either on the command line or in your tsconfig.json:

{
  "compilerOptions": {
    "experimentalDecorators": true,
    "emitDecoratorMetadata": true,
  }
}
import 'reflect-metadata';
import { mapping, map, select } from 'auto-mapping';

class Person {
    @mapping({ type: String })
    name: string;
    @mapping(Boolean) // short form
    gender: boolean;
    @mapping({ path: 'others.number', type: Number })
    age: number;
    extraData = 1;
}
const data={
    name: 'fisher',
    gender: 1,
    others: { number: '18' },
};
console.info("Mapped result:", map(data, Person));
console.info("Selected result:", select(data, Person));
# output
Mapped result: Person { extraData: 1, name: 'fisher', gender: true, age: 18 }
Selected result: { name: 'fisher', gender: true, age: 18 }

Use with reflect-metadata

If you have already imported reflect-metadata module into your project, it will infer type automatic except array type. The array type must declare the type parameter at any time.

import 'reflect-metadata';
import { mapping, map } from 'auto-mapping';

class Person {
    @mapping()
    name: string;
    @mapping()
    gender: boolean;
    @mapping({ path: 'others.number' })
    age: number
}
const data={
    name: 'fisher',
    gender: 1,
    others: { number: '18' },
};
console.info(map(data, Person));
# output
Person { name: 'fisher', gender: true, age: 18 }

API

@mapping(options: object | function)

Configuring property mapping information, If the argument is a function, then it is equivalent to { type: options }.

  • domain(string): The parent path of current property, the option domain will be ignored when used with path.
  • type(function | [function]): The property decalre type, it is always necessary if the property type is an array. Tt is optional if the module 'refleat-metadata' has been imported in your project. It also can be used to set custom conversion function, it will use default value if the conversion throws an error.
  • path(string): The property path in the source object, such as 'a.b.c','a.b[0].c', default is the current path name. Use the dot symbol ('.') to indicates the current path.
  • order(number): The order for the property generated, default is 0.
  • source(string): The source object name, it is required if you want to map data from multiple data sources.
  • default(any): Default value, multiple data sources can specify multiple different default values.

map(source:object, constructor:function, options?:object)

Map an object to an instance of the specified type.

  • source(object): Data source object
  • constructor(function): The type of instance, the constructor function of the class
  • options(string, optional): Mapping options.
    • source(string): The source object name, it is required if you want to map data from multiple data sources.
    • useDefaultSource(boolean): Use the default mapping configuration when the current source configuration is missing, default is true.
    • nullable(boolean): Whether to allow the value to be set to null.
    • allowNaN(booelan): Whether to allow the value to be set to NaN.
    • converters(Map): Custom conversion function, only valid during the current mapping.

map.setDefaultConverter(type:function, converter:function)

Set the global conversion function for the specified type

  • type(function): The type of value to convert.
  • converter(function): The conversion function.

The function setDefaultConverter will affect the global type conversion. If you want to use it locally, please set the converters option of the map function.

import "reflect-metadata";
import { mapping, map } from "auto-mapping";

map.setDefaultConverter(String, (val) => (val === undefined || val === null) ? '' : String(val));
map.setDefaultConverter(Number, (val) => isNaN(Number(val)) ? 0 : Number(val));

class Person {
    @mapping()
    public cityName: string;
    @mapping(() => "default")
    public intro: string;
    @mapping()
    public num: number;
}

console.info(map({}, Person));
# output
Person { name: '', desc: 'default', num: 0 }

Array

Because the type of the array cannot be automatically derived, the type parameter must be specified at all times and the value must be an array.

import { mapping, map } from 'auto-mapping';

class ArrayTest {
    @mapping({ type: [Number] })
    numbers: number[];
}
const data={
    numbers:['1','2','3']
};
const result = map(data, ArrayTest);
console.info(result);
# output
ArrayTest { numbers: [ 1, 2, 3 ] }

If an property is declared as array, but the source value is not an array, the result is wrapped into an array.

import { mapping, map } from 'auto-mapping';

class ArrayTest {
    @mapping({ type: [Number], path:'number' })
    numbers: number[];
}
const data={
    number: '1'
};
console.info(map(data, ArrayTest));
# output
ArrayTest { numbers: [ 1 ] }

Multiple Data Source

The default data source mapping config named default, you can set multiple configurations by source option to map multiple data sources.

import { mapping, map } from 'auto-mapping';

class Person {
    @mapping()
    @mapping({ source: 'other', path: 'person.name' })
    name: string;
}
const dataSource1 = {
    name: 'fisher'
};
const dataSource2 = {
    person: { name: 'jack' }
};
const result1 = map(dataSource1, Person);
const result2 = map(dataSource2, Person, { source: 'other' });

console.info(result1, result2);
# output
Person { name: 'fisher' } Person { name: 'jack' }

Custom Conversion

The signature of the custom conversion function is as follows:

function(value: any, source: any, dest: any, options?: object){}
  • value(any): The value which current path matched in the source object;
  • source(any): The source object;
  • dest(any): The instance of the dest class;
  • options(object, optional): Mapping options;

Custom conversion function can be set by type parameter. If you just pass a function to the annotation, like this mapping(fn), then it is equivalent to mapping({ type: fn }).

import { mapping, map } from 'auto-mapping';

function trim(value: string) {
    return value && value.trim();
}

function fullName(_value: any, source: any) {
    return `${source.firstName} ${source.lastName}`;
}

function info(_value: any, _source: any, dest: Person) {
    return `I am ${dest.name} who come from ${dest.city}`;
}

class Person {
    age: number = 18;
    @mapping(fullName)
    name: string;
    @mapping({ type: trim, path: 'city.cityName' })
    city: string;
    @mapping(info)
    info: string;
}
const dataSource = {
    firstName: 'Lei',
    lastName: 'Lee',
    city: {
        cityId: 1,
        cityName: '    NEW YORK    '
    },
};
console.info(map(dataSource, Person));
# ouput
Person {
  age: 18,
  name: 'Lei Lee',
  city: 'NEW YORK',
  info: 'I am Lei Lee who come from NEW YORK.' 
}

Extensions

[MAPPING](src:object, options?:object){...}

Do some processing before mapping.

import "reflect-metadata";
import { mapping, map, MAPPING } from "auto-mapping";

class Person {
    @mapping()
    public num: number = 1;
    @mapping()
    public name: string;
    [MAPPING](src: any, options: any) {
        src.name = "fisher";
    }
}
const result = map({ num: 10 }, Person);
console.info(result, result instanceof Person);
# output
Person { num: 100, name: 'fisher' } true

If the MAPPING function returns a value that is not undefined, it will be passed to the map method as a source object.

[MAPPED](src:object, options?:object){...}

Sometimes you may need to do some extra finishing work, such as dynamically adding some properties, you can do this through the MAPPED symbol function.

import "reflect-metadata";
import { mapping, map, MAPPED } from "auto-mapping";

class Person {
    @mapping()
    public gender: boolean;
    @mapping()
    public num: number = 1;
    [MAPPED](src: any, options: any) {
        // set value manually
        this.gender = true;
        // override original value
        this.num = 100;
        // dynamic assignment
        Object.assign(this, { a: 1, b: 2 })
    }
}
const result = map({ num: 10 }, Person);
console.info(result, result instanceof Person);
# output
Person { num: 100, gender: true, a: 1, b: 2 } true

:warning: Note that when using the MAPPED function, if the map result is null, then any property that accesses this will throw an error.

If the function returns a value that is not undefined, it will replace the map result value.

...
[MAPPED](src: any, options: any) {
    this.gender = true;
    this.num = 100;
    return Object.assign({}, this, { a: 1, b: 2 })
}
...
# output
{ num: 100, gender: true, a: 1, b: 2 } false

Update Logs

1.3.0

  • added select function.

1.2.1

  • compatible for mini-programs.

1.2.0

  • added mapping options nullableallowNaNconverters;
  • added new api map.setDefaultConverter to config global default type conversion;

1.1.0

  • refacted module using reflect-metadata;
  • added two extension methods MAPPING and MAPPED;
  • marked the after method as a deprecated method and replaced with the MAPPED method.