Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to combine multiple property decorators in Typescript?

I have a class Template with a property _id which has decorators from class-transformer and typed-graphql

import {classToPlain, Exclude, Expose, plainToClass, Type } from 'class-transformer';
import { ExposeToGraphQL } from '../../decorators/exposeToGraphQL';
import { Field, ID, MiddlewareInterface, NextFn, ObjectType, ResolverData } from 'type-graphql';
import { getClassForDocument, InstanceType, prop, Typegoose } from 'typegoose';
/**
  * Class
  * @extends Typegoose
  */
@Exclude()
@ObjectType()
class Template extends Typegoose {
  // @Expose and @Type should be both covered by ExposeToGraphQL
  // @Expose()
  @Type(() => String)
  @ExposeToGraphQL()
  @Field(() => ID)
  public _id?: mongoose.Types.ObjectId;
}

Now I try to combine those two into a new custom property decorator:

/**
 *
 */
import { Expose } from 'class-transformer';
import 'reflect-metadata';

const formatMetadataKey: Symbol = Symbol('ExposeToGraphQL');

function ExposeToGraphQL() {
  console.log('ExposeToGraphQL');

  return Expose();
}

function getExposeToGraphQL(target: any, propertyKey: string) {
  console.log('getExposeToGraphQL');

  return Reflect.getMetadata(formatMetadataKey, target, propertyKey);
}

export {
  ExposeToGraphQL,
  getExposeToGraphQL,
};

The custom decorator works if I only return the result of Expose(), but I don't know how to combine @Expose and @Type in @ExposeToGraphQL().

like image 930
user547995 Avatar asked Aug 22 '18 14:08

user547995


People also ask

How many decorators can be applied to a declaration?

It's possible to use as many decorators on the same piece of code as you desire, and they'll be applied in the order that you declare them. This defines a class and applies three decorators — two to the class itself, and one to a property of the class: @log could log all access to the class.

Are decorators still experimental in TypeScript?

Decorators are a stage 2 proposal for JavaScript and are available as an experimental feature of TypeScript. NOTE Decorators are an experimental feature that may change in future releases.

What is target in TypeScript Decorator?

The first parameter is commonly called target . The sealed decorator will be used only on class declarations, so your function will receive a single parameter, the target , which will be of type Function . This will be the constructor of the class that the decorator was applied to.

What is the third argument in a method Decorator expression?

The expression for the method decorator function accepts three arguments. They are: Either the constructor function of the class for a static member or the prototype of the class for an instance member. The member name. The Property Descriptor for the member.


1 Answers

import { Expose, Type, TypeOptions, ExposeOptions } from 'class-transformer';

/**
 * Combines @Expose then @Types decorators.
 * @param exposeOptions options that passes to @Expose()
 * @param typeFunction options that passes to @Type()
 */
function ExposeToGraphQL(exposeOptions?: ExposeOptions, typeFunction?: (type?: TypeOptions) => Function) {
  const exposeFn = Expose(exposeOptions);
  const typeFn = Type(typeFunction);

  return function (target: any, key: string) {
    typeFn(target, key);
    exposeFn(target, key);
  }
}

Then you can use that decorator as follow:

class Template extends Typegoose {
    @ExposeToGraphQL(/*exposeOptions*/ undefined, /*typeFunction*/ () => String)
    @Field(() => ID)
    public _id?: mongoose.Types.ObjectId;
}

You can find official documentation for decorator in this link.

@Expose and @Type() are basically Decorator Factories. The main purpose of a decorator factory:

  • it return a function
  • that function will be called at runtime (right after the class, in this case is Template, was defined) with 2 arguments:
    • class prototype (Template.prototype)
    • name of the property which the decorator attached to (_id).

If two or more decorators are attached to a same property (called as Decorator Composition), they are evaluated as follow:

  • The factory functions are executed in the same order as they are written in code
  • The functions returned by factory functions are executed in reversed order
like image 165
toancaro Avatar answered Oct 11 '22 14:10

toancaro