TS

TypeScript is a free and open source programming language developed by Microsoft. It is a superset of JavaScript, and essentially adds optional static classes and class-based object-oriented programming to the language. TypeScript extends JavaScript's syntax so that any existing JavaScript program can work in TypeScript unchanged. TypeScript is designed for large application development and generates JavaScript at compile time to ensure compatibility. I pulled this off the InternetCopy the code

Why encapsulate Express with TS?

The main purpose is to use the decorator in TS. Although decorator is a proposal in ES7, it still needs to be configured with Babel to use it in JS.Copy the code

Let’s start with what is a decorator

Decorators only provide definition hijacking. The ability to define classes and their methods, method inputs, and attributes does not provide any additional metadata. The main purpose is to provide additional functionalityCopy the code

So how to use the decorations in TS

NPM init -y // use default configuration NPM I typescript -s // install ts package TSC --init // initialize TS configuration mkdir SRC // create SRC directory mkdir decorators Ts // Create common for discrimination and decorator touch index.ts // create index file so far the basic architecture of the project has been set upCopy the code

Let’s talk about what types of decorators are available in TS

Class decorator

The constructor argument applied to the class: target (class) Function classDecorators (target: any) {/ / we operated in this place to object / / than I we add something to the prototype of the class target. The prototype. Usernmae = 'silent'}Copy the code

Attribute decorator

Decorator arguments applied to the property: targte (class), name (name of the current property)Copy the code
function attrDecorators(target:any,name:string){
    target[name]=150 // We assign a value to the current attribute
}
Copy the code

Method decorator

Decorator parameters applied to methods: targte (class), name (name of the current method),descriptor (descriptor of the current method)Copy the code
function funcDecorators(target:any,name:string,descriptor:PropertyDescriptor){
    // Let's write some inside the decorator and print a time each time we execute
    let origin=descriptor.value;// This is the content of the current method, let's save it
    descriptor.value=function(){
        console.log(new Date())
        // Execute the method we saved. Arguments are the arguments passed in
        return origin.apply(this.arguments)}return descriptor
}
Copy the code

Decorator for method parameters

Decorator arguments applied to method arguments: target (class), name (method name of the current argument), index (index of the current argument in the parameter list)Copy the code
function paramDecorators(target:any,name:string,index:number){
	// This decorator is not as good as the example above
}
Copy the code

This is the ts decorator we talked about. Let’s add it to a class to see if its execution order is the same as above

We can see that property decorators go first, then parameters, then methods, then classes

I see others decorator inside can pass value, why I see you this can’t!

Don't worry, we'll get to that in a second, there's also a decorator in TS called the Decorator Factory that does just that. The decorator factory definition method returns a method for exampleCopy the code
function classDesc(path:string){
  // This is a decorator factory, where path is the value you can pass in
  console.log(path,'class')
  return (target:any) = >{
      console.log(typeof target,'class')}}Copy the code
Other types of decorators are written similarly, but with different parametersCopy the code

Next, let’s talk about metadata in JS

Metadata is data that describes data, or annotations, that provides support for additional metadata and doesn't do anything. A separate Scanner is required to perform operations based on the metadata. But I heard the Java annotations like what's inside To sum up, a decorator and annotations are different, I have talked about above the concept of a decorator, mainly to provide additional functionality, annotation does not provide these, it only provides metadata support, anything can't operation, so only based on metadata to realize the specific operation.Copy the code

Now that we know the metadata we can encapsulate the decorators described above, encapsulate the ones that need to be used

We will eventually implement these decorators
Controller, Get, Post, Body, Query, etc. Before we create common.ts, let's create our first decorator here.Copy the code
const PATH_METADETA='path';
const PATH_METADETA = "path";
const PARAMS_METADETA = "params";
function Controller(path:string) :ClassDecorator{
	// Create a decorator factory that can be used to receive parameters, as described above
  return (target) = >{
  	/* Add metadata to the current class reflect.definemetadata is used to add metadata. There are four values in total. The first is key, which is unique, the second is the value, the third is the current object, and the fourth is the method name. You don't have to fill in the method why do you have metadata, decorators are meant to extend methods, but they're meant to extend this method, and we don't get the specific data in this decorator, so we use metadata to set the specific data, register it in general elsewhere, This decorator is used to set metadata
  	Reflect.defineMetadata(PATH_METADETA, path, target);// The current decorator is passed
      // The data is set into the metadata of the class, which does not affect our daily use}}// We need to write methods like Post and Get. Essentially these methods are different except for Post and Get
// It is the same
function createdMethod(methods: string) {
  return (path: string) :MethodDecorator= > {
    return (target: any, key: string | symbol, descriptor: any) = > {
      // Set one for the current method
      Reflect.defineMetadata(PATH_METADETA, path, target, key);
      // Add method metadata describing the current method
      Reflect.defineMetadata(METHOD_METADETA, methods, target, key);
    };
  };
}
Copy the code
For now we'll start with these two decorators, and then we'll start with params decoratorsCopy the code

The decorator was written

But where are we going to use it? It can't be that easy. Of course it won't be that easy, we now need a place to register, and we need express to get the metadata that the decorator decorates. We are building a file in decorators called register.ts to register our decorated class. Here is the codeCopy the code
import {Router} from 'express';
function register(app:any,controller:Array<any>){
	const router=Router();
 	controller.forEach((v:any) = >{
         // This v is the class
         // Get the metadata of the current class key as path, which was set earlier by our decorator
         let classPath=Reflect.getMetadata('path',v);
         // Iterate over the current property to see what metadata exists on the current class property
         for(const key in new v()){
         	// The key in getMetadata is the name of the method being iterated through
             // Get the metadata set for the current method
             let attrMethod = Reflect.getMetadata('method'.new v(), key);
             let attrPath = Reflect.getMetadata('path'.new v(), key);
             // We are defining a callback method, this is the express callback method
             let fn:any=(req,res,next) = >{
             	// We will execute our current method by passing two arguments (this method is the following method in @Post@Get).
               let result=new v()[key].apply(new v(),[req,res]);
               if (result instanceof Promise) {
               	result.then(value= > {
               		!res.headersSent && res.send(value);
               	}).catch(err= > {
               		next(err)
               	})
               } else if(result ! = =undefined) {! res.headersSent && res.send(result); }}// Assemble the route path
             let routerParams = [classPath + attrPath]
             // Add a callback
             routerParams.push(fn)
             // Call the router method
             router[attrMethod].apply(router, routerParams)
         }
         // To solve the body
         app.use(bodyParser.json())
         app.use(bodyParser.urlencoded({ extended: false }))
         app.use(router)// Use routing})}Copy the code
Start Express and register in index.ts, and our decorator should be able to interact with the RouterCopy the code
  register(app,[UserController]);

  var server = app.listen(3122.function () {
      var host = server.address().address;
      var port = server.address().port;
      console.log('Example app listening at http://%s:%s', host, port);
  })
Copy the code
This is the end of it! I also studied a whole afternoon, looked at github above some mainstream framework source code, stripped out of the simple version I am also a did not do a long time small rookie, Hope this article] chapter help myself to write to you of the project in [making] (https://github.com/mrzhouxl/express-decorator)Copy the code