import { ClassConstructor, Expose, Transform, TransformFnParams } from 'class-transformer' import 'reflect-metadata' import { typeTransformer } from '../transformer' export interface Field { name?: string fieldName?: string description?: string type?: any transform?: (params: TransformFnParams) => any, // onDeserialize: ignore current field when deserialization // onSerialize: ignore current field when serialization ignore?: { onDeserialize?: boolean, onSerialize?: boolean } | boolean } const FIELD_KEY = Symbol('FIELD') const FIELDS_KEY = Symbol('FIELDS') export function getFieldList>(cls: T): (keyof InstanceType)[] { return Reflect.getMetadata(FIELDS_KEY, cls) || [] } function addFieldItem>(cls: T, field: keyof InstanceType) { Reflect.defineMetadata(FIELDS_KEY, [...new Set([...getFieldList(cls), field])], cls) } export function getField>(cls: T, field: keyof InstanceType): Field { return Reflect.getMetadata(FIELD_KEY, cls.prototype, field as string) } export function getFields>(cls: T): Record, Field> { return getFieldList(cls).reduce((map, field) => { map[field] = getField(cls, field) return map }, {} as Record, Field>) } export default function (conf: Field) { return function (prototype: Object, propertyKey: string | symbol) { if (!Reflect.has(conf, 'type')) { conf.type = Reflect.getMetadata('design:type', prototype, propertyKey) } addFieldItem(prototype.constructor as ClassConstructor, propertyKey) Reflect.defineMetadata(FIELD_KEY, conf, prototype, propertyKey) if (!Reflect.has(conf, 'transform')) { Transform(typeTransformer(conf.type))(prototype, propertyKey) } else { Transform(conf.transform!)(prototype, propertyKey) } if (conf.fieldName) { Expose({ name: conf.fieldName })(prototype, propertyKey) } } }