Typescript best practices

summary

Deep understanding of TypeScript

TypeScript has a type system and is a superset of JavaScript It can be compiled into ordinary JavaScript code TypeScript is a more Java Script than JavaScript;

install

npm install typescript -g

tsc

#   Version 3.9.7
#   Syntax:   tsc [options] [file...]
#   
#   Examples: tsc hello.ts
#             tsc --outFile file.js file.ts
#             tsc @args.txt
#             tsc --build tsconfig.json

data type

In typescript, in order to make the code more standardized and more convenient for maintenance, type verification is added;

  • Boolean type
//  boolean type
var flag:boolean = true;
  • Number type (number)
//  Number type
let num:number = 123;
  • String type (string)
//  String type
let str:string = "aaa";
  • Array type (array)
/*
 *  There are two ways to array types
 */

// Specifies that the array type is number
var arr:number[] = [];

// Define the array in a generic way, and specify that the array element is of type number
var arr:Array<number> = [];
  • Tuple type
//  Tuple type is also a kind of array, 
var arr:[string,number,boolean] = ["",123,true];
  • Enumeration type (enum)
//  Enumeration type enum by default, the key value starts from 0; If the attribute is not assigned a value, it is accumulated from 0;
enum Flag{
    success = 1,
    error = 0
}
var f:Flag = Flag.success;

enum Color{ red, blue, green }
var c:Color = Color.red;
console.log(c);         // 0
  • Any type (any)
//  Any type, you can modify the value type repeatedly
var a:any = 123;
a = "hello";
a = false;
  • null and undefined
//  By default, null and undefined are subtypes of all types;
var num:number | undefined;
console.log(num);       // undefined

num = 123;
  • void type
//  Indicates that there is no type, which is commonly used for the return value type of a function
function print(str:string):void{
    console.log(str);
}
print("hello");

Type Asserts

Type assertion is the same as type conversion in other strongly typed languages, but it does not carry out special data inspection and deconstruction;

// str is of any type;
var str:any = "this is a string";
// Convert str of any type to string type, and take the length of string;
let len:number = (<string>str).length;

as syntax can also be used for the above writing:

var str:any = "this is a string";
var len:number = (str as string).length;

class

Definition and inheritance of classes in es5

Review the definition of classes in es5;

// Class constructor
function Person(){
    // Properties of class
    this.name = "xxx";
    this.age = 24;

    // Class method
    this.run = function(){
        alert(this.name + "In motion~");
    }
}

// You can also add class properties and methods through prototype chains
Person.prototype.sex = "male";
Person.prototype.working = function(){
    alert(this.name + "at work~");
}

// Class
Person.getInfo = function(){
    console.log("This is a static property of the class~");
}

var p = new Person();
p.run();

Person.getInfo();

// Class inheritance prototype chain and object impersonation
function Student(){
     // Object impersonation implements inheritance. Object impersonation can only inherit from the attributes and methods defined in the constructor, but cannot inherit the attributes and methods above the prototype chain;
    Person.call(this);  
}
var w = new Student();
w.run();
w.working();            //  error

// The prototype chain can inherit the properties and methods in the constructor, or the properties and methods above the prototype chain; 
// However, if the constructor of the parent class needs to pass parameters, there is no way to pass parameters to the parent class when instantiating a child class
Student.prototype = new Person();

Implement class inheritance in combination

function Person(name, age){
    this.name = name;
    this.age = age;

    this.run = function(){
        alert(this.name + "In motion~");
    }
}

Person.prototype.sex = "Male";
Person.prototype.working = function(){
    alert(this.name + "at work~");
}
Person.getInfo = function(){
    alert("This is the static method of the class~");
}

function Student(name, age){

    Person.call(this,name,age);
}
// Student.prototype = new Person();
Student.prototype = Person.prototype;

var s = new Student("Tonny",25);
s.run();
s.working();

Definition and inheritance of typescript class

Class definition in typescript;

class Person{
    name:string;
    age:number;
    constructor(name:string,age:number){
        this.name = name;
        this.age = age;
    }

    run():void{
        alert(this.name + "In motion~");
    }

    getName():string{
        return this.name;
    }

    setName(name:string):void{
        this.name = name;
    }
}

var p:Person = new Person("Tom",26);
p.run();

class Student extends Person{
    constructor(name:string,age:number){
        super(name,age);
    }

    working():void{
        alert(this.name + "at work~");
    }
}

var s:Student = new Student("Kangkang",25);
s.run();
s.setName("Hope");

Class modifier

When defining attributes in typescript, it provides us with access modifiers;

  • Public: public. It can be accessed inside the class, subclass and outside the class;
  • protected: protection type. It can be accessed in the class and subclass, but not outside the class;
  • Private: private, which can only be accessed inside the current class;

If no modifier is added, the default is public access level;

class Person{
    private name:string;
    private age:number;
    //  `Static ` keyword. You can define the attributes and methods in a class as static attributes and methods of a class
    public static sex:string = "Male";
    constructor(name:string,age:number){
        this.name = name;
        this.age = age;
    }
    public run():void{
        alert(this.name + "In motion~");
    }

    public setName(name:string):void{
        this.name = name;
    }
}

var p:Person = new Person("Tonny",22);
console.log(Person.sex);

abstract class

Abstract keyword defines an abstract class. This class requires that its subclasses inherit from it and must implement the abstract methods of the abstract class;

abstract class Animal{
    abstract eat():void;
}

class Dog extends Animal{
    constructor(){
        super();
    }

    eat():void{
        
    }
}

Abstract classes are used as the base classes of other derived classes. The syntax is a bit similar to interface methods. Both define method signatures but do not contain method bodies. For example, the above Animal abstract class defines eat abstract methods. Subclasses inherited from it must implement eat methods;

Interface

In object-oriented programming, interface is a definition of specification, which defines the specification of behavior and action. In programming, interface plays a role of restriction and specification. Interface defines the specification that a certain batch of classes need to comply with. Interface does not care about the internal state data of these classes or the implementation details of methods in these classes, It only stipulates that certain methods must be provided in this batch of classes, and the classes that provide these methods can meet the actual needs;

The interface in typescript is similar to java, and more flexible interface types are added, including attributes, functions, indexable and classes

Attribute interface

interface FullName{
    firstName:string,
    secondName:string,
    age?:number
}

function printName(name:FullName){
    console.log(name.firstName + "---" + name.secondName);
}

var obj:FullName = {
    firstName:"zhang",
    secondName:"san"
}

printName(obj);

interface keyword can restrict function parameters. In the above code, we need the printName function to receive a json object containing firstname and secondname; If the parameters passed in do not contain these two fields, the program will prompt an error;

In addition, you can see age? It is an optional parameter, not necessarily passed in;

Function type interface

Function type interface: constrains the parameters and return values passed in by the method;

interface Encrypt{
    (key:string, value:string): string
}

var md5:Encrypt = function(key:string,value:string):string{

    return encodeURI(key + "%$?^(*@&" + value);
}

console.log(md5("name","zhangsan"));

Indexable interface

Indexable interface is an interface that constrains arrays and objects;

// Constraints on arrays
var arr1:number[] = [1,2,3,5];

var arr2:Array<string> = ['1','hahe','akon'];


interface UserArray{
    // The index of the array is of type number and the index value is of type string
    [index:number]:string
}

var arr:UserArray = ["jiakang","kongkon"];
var arr:UserArray = [12,56];            // report errors

Class type interface

Constraints on classes, and abstract class A little similar;

interface Animal{
    name:string,
    eat(str:string):void,
}

// Class needs to implement the class interface
class Dog implements Animal{
    name:string;

    constructor(name:string){
        this.name = name;
    }

    eat():void{

    }
}

var d:Dog = new Dog("xiaogou");

The implements keyword is applicable to the specification of the class to implement the interface; In the above code, the Dog class implements the definition of the Animal interface;

generic paradigm

In the development, we should not only consider the consistent and well-defined api, but also consider the reusability; The component can support not only the current data type, but also the future data type, which can provide very flexible functions when developing large-scale systems;

The popular understanding of generics is to solve the reusability of classes, interfaces and methods and support for unspecified data;

function GetData<T>(value:T):T{

    return value;
}

GetData<number>(123);
GetData<string>("aaa");

The generic function requires that the type be specified when calling the function. The GetData function above indicates that the incoming parameter type is consistent with the returned parameter type;

Generic class

class MinxNum{
    public list:Array<number> = [];

    add(num:number):void{
        this.list.push(num);
    }

    min():number{
        var n = this.list[0];
        for(let i = 0; i < this.list.length; i++){
            if(n > this.list[i]){
                n = this.list[i];
            }
        }
        return n;
    }
}

var m:MinxNum = new MinxNum();

m.add(5);
m.add(3);
m.add(1);

console.log(m.min());       // 1

The MinxNum class above can obtain the minimum value of the passed in parameters, but one defect is that it can only return number type; If we still need to return the minimum character between a-z characters, we need to modify this tool class again;

class MinxNum<T>{
    public list:Array<T> = [];

    add(num:T):void{
        this.list.push(num);
    }

    min():T{
        var n = this.list[0];
        for(let i = 0; i < this.list.length; i++){
            if(n > this.list[i]){
                n = this.list[i];
            }
        }
        return n;
    }
}

var m = new MinxNum<number>();

m.add(5);
m.add(3);
m.add(1);

console.log(m.min());       // 1

var n = new MinxNum<string>();

n.add("n");
n.add("a");
n.add("d");

console.log(n.min());       // a

Generic Functions

interface Config<T>{
    (value:T):T;
}

function GetData<T>(value:T):T{

    return value;
}

var d:Config<string> = GetData;

d("aaa");

Generic class constraints

Generics can help us avoid duplicate code and support for unspecified data types (type verification). We can take classes as generic classes of parameters;

  • Define a class
  • Class as a parameter to constrain the type of data passed in
/**
 *  Define a user class, which is used to map the table fields of the database
 *  Define a Mysql class, which is used to operate the database
 *  Pass the user class into Mysql as a parameter
 */

class User{
    username:string;
    password:string;
}

class Mysql{

    add(user:User):boolean{
        // mysql query
        return true;
    }
}

var u:User = new User();
u.username = "zhangsan";
u.password = "aaa12345";


var db:Mysql = new Mysql();
db.add(u);

In this way, if we need to add an article class and insert the article into the database, the Mysql class cannot pass the User class as a parameter; You can use generics to make Mysql class into a general tool class to operate the database;

class User{
    username:string;
    password:string;
}

class ArticleCate{
    title:string | undefined;
    desc:string | undefined;
    status:number | undefined;
}

class Mysql<T>{

    add(info:T):boolean{
        // mysql query
        return true;
    }
}

var u:User = new User();
u.username = "zhangsan";
u.password = "aaa12345";


var art:ArticleCate = new ArticleCate();
art.title = "Big bang news";
art.desc = "This is an explosive news";
art.status = 0;


var db:Mysql<User> = new Mysql<User>();
db.add(u);


var artc:Mysql<ArticleCate> = new Mysql<ArticleCate>();
artc.add(art);

Typescript encapsulates DB

  • Function: define a database operation class, support MySQL and mongodb;
  • Requirements: the functions are the same. There are add, delete, update and find methods;
  • Note: code reusability and constraints are unified specifications
  • Idea: constraint specifications are required, so to define interfaces, code reuse is required, so generics are required
interface DBI<T>{
    add(info:T):boolean;
    update(info:T,id:number):boolean;
    delete(id:number):boolean;
    get(id:number):any[];
}

class MySql<T> implements DBI<T>{
    add(info: T): boolean {
        return true;
    }
    update(info: T, id: number): boolean {
        return true;
    }
    delete(id: number): boolean {
        return true;
    }
    get(id: number): any[] {
        return [];
    }
}

class MongoDB<T> implements DBI<T>{
    add(info: T): boolean {
        return true;
    }
    update(info: T, id: number): boolean {
        return true;
    }
    delete(id: number): boolean {
        return true;
    }
    get(id: number): any[] {
        return [];
    }
}

// Define a User class
class User{
    username:string | undefined;
    password:string | undefined;
}

var u:User = new User();
u.username = "zhangsan";
u.password = "aa123456";


// mysql 
var oMysql:MySql<User> = new MySql<User>();
oMysql.add(u);


// mongodb
var oMongo:MongoDB<User> = new MongoDB<User>();
oMongo.add(u);

Namespace

In the case of large amount of code, in order to avoid the conflict of various variable names, functions, classes and interfaces with similar functions can be placed in the namespace;

Same as Java package net namespace, typescript namespace can wrap the code, only expose the objects that need to be accessed externally, and the objects in the namespace are exported through the export keyword; The calling method uses the name of the namespace;

Difference between namespace and module: namespace belongs to internal module, which is mainly used to organize code and prevent code naming conflict; Modules focus on code reusability. There may be multiple namespaces in a module;

namespace A{
    interface DBI<T>{
        add(info:T):boolean;
        update(info:T,id:number):boolean;
        delete(id:number):boolean;
        get(id:number):any[];
    }
    
    export class MySql<T> implements DBI<T>{
        add(info: T): boolean {
            return true;
        }
        update(info: T, id: number): boolean {
            return true;
        }
        delete(id: number): boolean {
            return true;
        }
        get(id: number): any[] {
            return [];
        }
    }
    
    export class MongoDB<T> implements DBI<T>{
        add(info: T): boolean {
            return true;
        }
        update(info: T, id: number): boolean {
            return true;
        }
        delete(id: number): boolean {
            return true;
        }
        get(id: number): any[] {
            return [];
        }
    }
    
    // Define a User class
    export class User{
        username:string | undefined;
        password:string | undefined;
    }
}



var u:A.User = new A.User();
u.username = "zhangsan";
u.password = "aa123456";


var oMysql:A.MySql<A.User> = new A.MySql<A.User>();
oMysql.add(u);


var oMongo:A.MongoDB<A.User> = new A.MongoDB<A.User>();
oMongo.add(u);

Decorator

Decorator allows adding new functions to an existing object without changing its structure. This type of design pattern belongs to structural pattern, which is used as a wrapper for an existing class;

This pattern creates a decoration class to wrap the original class, and provides additional functions on the premise of maintaining the integrity of class method signature;

Decorator principle

Decorator is essentially a function, which will be called at run time, and the decorated declaration information will be passed in as parameters; Theoretically, if parameters are ignored, any function can be used as a decorator;

import * as Koa from 'koa';

function decrator(method){
    return (target,property,descriptor)=>{
        console.log('method',method);
        console.log('arguments',arguments);
        console.log('target',target);
        console.log('property',property);
        console.log('descriptor',descriptor);
    }
    /*
     *      method /get
     *      arguments [Arguments] { '0': '/get' }
     *      target User {}
     *      property list
     *      descriptor {
     *          value: [Function: list],
     *          writable: true,
     *          enumerable: false,
     *          configurable: true
     *      }
     * 
     */
}

export default class User {
    @decrator('/get')
    public list(ctx:Koa.Context){
        ctx.body = { ok:1 }
    }
}

Application scenario of decorator

Decorators can be attached to class declarations, methods, properties or parameters to modify the behavior of the class;

  • AOP section application:

    • Static injection mode
    • Reflection mechanism
  • Open closed principle, class decoupling:

    • MVC permission judgment
    • annotation

Decorator type

Common decorators include class decorator, attribute decorator, method decorator and parameter decorator

Writing method of decorator: ordinary decorator (can't be transferred to reference), decorator factory (can be transferred to reference)

Class decorator
    1. Parameterless decorator
function logClass(category:any){
    console.log('category',category);
    category.prototype.apiUrl = 'http://www.baidu.com';
    category.prototype.func = function(){};
}

@logClass
class HttpClient{
    constructor(){}
}

const http = new HttpClient();
console.log(http['apiUrl']);

//  category [Function: HttpClient]
//  http://www.baidu.com
    1. With parameter decorator (factory mode)
function logClass(params:any){
    console.log('params',params);
    return (target)=>{
        target.prototype.apiUrl = params;
        target.prototype.func = function(){};
    }
}

@logClass('http://www.baidu.com')
class HttpClient{
    constructor(){}
}

const http = new HttpClient();
console.log(http['apiUrl']);

//  params http://www.baidu.com
//  http://www.baidu.com
Method decorator
    1. Parameterless decorator
function logClass(target, name, descriptor){
    var oldValue = descriptor.value;
    descriptor.value = function(){
        console.log(`Calling "${name}" with`, arguments);
        return oldValue.apply(null, arguments);
    }
}

class Maths{
    constructor(){

    }

    @logClass
    add(a,b){
        return a + b;
    }
}

const user = new Maths();
console.log(user.add(3,5));

//  Calling "add" with [Arguments] { '0': 3, '1': 5 }
//  8
    1. Decorator factory (can be transmitted for reference)
function fnMethod(params){
    return (target, property, descriptor)=>{
        // Save the old method
        const oMthod = descriptor.value;
        // Rewrite old methods
        descriptor.value = function(a,b){
            // Call an overridden method
            //oMthod.apply(null,...arguments);
            return (a + b) * params;
        }
    }
}

class Maths{
    constructor(){}

    @fnMethod(5)
    add(a,b){
        return a + b;
    }
}

const fnObj = new Maths();
console.log(fnObj.add(3,5));

//  40
Attribute decorator

The property decorator expression will be called as a function at runtime, passing in the following 2 parameters:

  • For static members, it is the constructor of the class, and for instance members, it is the prototype object of the class;
  • Attribute name;
function fnProperty(value:any){
    return (target,attr)=>{
        console.log('target',target);
        console.log('attr',attr);
        target[attr] = value;
    }
}

class Person{
    @fnProperty('laowang')
    public name:string
}

const p = new Person();
console.log(p.name);

//  target Person {}
//  attr name
//  laowang

Finisher execution sequence

function anotationClass(args:any){
    console.log("anotationClass evaluated",args);
    return (target)=> console.log('anotationClass executed',target);
}

function anotationMethods(args:any){
    console.log('anotationMethods evaluated', args);
    return (target, property, descriptor) => console.log('anotationMethods executed',target);
}

function anotationProperty(args:any){
    console.log('anotationProperty evaluated', args);
    return (target, attr) => console.log('anotationProperty executed',target);
}

@anotationClass(0)
@anotationClass(1)
class Person{

    @anotationProperty('0')
    @anotationProperty('1')
    public name:string

    @anotationMethods(0)
    @anotationMethods(1)
    getName(){
        return this.name
    }
}

const p = new Person();

/*
 *      anotationProperty evaluated 0
 *      anotationProperty evaluated 1
 *      anotationProperty executed Person {}
 *      anotationProperty executed Person {}
 *      anotationMethods evaluated 0
 *      anotationMethods evaluated 1
 *      anotationMethods executed Person {}
 *      anotationMethods executed Person {}
 *      anotationClass evaluated 0
 *      anotationClass evaluated 1
 *      anotationClass executed [Function: Person]
 *      anotationClass executed [Function: Person]
 */

Execution order: attribute -- > method -- > class

Typescript decorator practice

NodeJs + Typescript + Koa decorator to realize background api development;

Project initialization
npm init -y
npm install typescript ts-node-dev tslint @types/node -D

Modify package JSON is:

"scripts": {
    "start": "ts-node-dev ./src/index.ts -P tsconfig.json --no-cache",
    "build": "tsc -P tsconfig.json && node ./dist/index.js",
    "tslint": "tslint --fix -p tsconfig.json"
}

Create tsconfig.config in the root directory json:

{
    "compilerOptions": {
        "outDir": "./dist",
        "target": "es2017",
        "module": "commonjs",                   //Organization code method
        "sourceMap": true,
        "moduleResolution": "node",             // Module solution strategy
        "experimentalDecorators": true,         // Open decorator definition
        "allowSyntheticDefaultImports": true,   // Allow es6 import
        "lib": [
            "es2015"
        ],
        "typeRoots": [
            "./node_modules/@types"
        ],
    },
    "include": [
        "src/**/*"
    ]
}
Project base code
    1. Installation dependency:
npm i koa koa-static koa-body koa-xtime glob -S
    1. Create a new index. In the src directory ts:

src/index.ts

import * as Koa from 'koa';
import koaBody, * as bodify from 'koa-body';
import * as serve from 'koa-static';
import * as timming from 'koa-xtime';

const app:Koa = new Koa();

app.use(timming());
app.use(serve(`${__dirname}/public`));

app.use(bodify());
app.use((ctx:Koa.Context)=>{
    ctx.body = "Hello ts-koa";
})

app.listen(3001,()=>{
    console.log("The server started successfully~");
});
npm start
Implement route definition and discovery

Requirements: define a decorator to realize the automatic registration of router;
Requirements: @ get is the request method for routing, and @ post is the request method for routing; When the prefix is passed in, the routing rule used for the pair is prefix/xxx, for example: api/users;

    1. For route discovery and registration, create util / route decors under the src directory ts:

src/util/route-decors.ts

import * as glob from 'glob';
import * as Koa from 'koa';
import * as KoaRouter from 'koa-router';
type HTTPMethod = 'get' | 'put' | 'del' | 'post' | 'patch';
type LoadOptions = {
    /**
    * Routing file extension. The default value is ` {js,ts}`
    */
    extname?: string;
};
type RouteOptions = {
    /**
    * It is applicable to the case where a request is special and needs to be prefixed separately
    */
    prefix?: string;
    /**
    * Add one or more middleware to the current route
    */
    middlewares?: Array<Koa.Middleware>;
};
const router = new KoaRouter();
const decorate = (method: HTTPMethod, path: string, options: RouteOptions = {}, router: KoaRouter) => {
    return (target, property: string) => {
        const url = options.prefix ? options.prefix + path : path
        router[method](url, target[property])
    }
}
const method = method => (path: string, options?: RouteOptions) => decorate(method, path, options, router);

export const get = method('get');
export const post = method('post');
export const put = method('put');
export const del = method('del');
export const patch = method('patch');

export const load = (folder: string, options: LoadOptions = {}): KoaRouter => {
    const extname = options.extname || '.{js,ts}';
    glob.sync(require('path').join(folder,`./**/*${extname}`)).forEach((item) => require(item))
    return router;
}
    1. Create a route and create a new route / user under src ts:

src/routes/user.ts

import * as Koa from 'koa';
import { get , post} from '../util/route-decors';

export default class User {
    @get('/list',{ prefix:'/user' })
    public pageList(ctx:Koa.Context){
        ctx.body = {
            state:"success",
            message:"Request succeeded~",
            data:{}
        }
    }
}
    1. Use the loader to read out all the routes under the routes directory and register them on the app:

index.ts

import * as Koa from 'koa';
import { load } from './util/route-decors';
import { resolve } from 'path';

const app:Koa = new Koa();

const router = load(resolve(__dirname,'./routes'));
app.use(router.routes());

app.listen(3001,()=>{
    console.log("The server started successfully~");
});
npm start

# http://127.0.0.1:3001/user/list
data verification

Above, we reserved a middlewares parameter for the decorator of the route, which can verify the data by using the mechanism of the middleware;

    1. Firstly, the route decors are transformed:
//...
const decorate = (method: HTTPMethod, path: string, options: RouteOptions = {}, router: KoaRouter) => {
    return (target, property: string) => {
        const middlewares = [];
        // If there's a message in the decorator
        if(options.middlewares){
            middlewares.push(...options.middlewares);
        }
        
        const url = options.prefix ? options.prefix + path : path
        // You also need to put the normal business middleware into it
        middlewares.push(target[property]);
        router[method](url, ...middlewares);
    }
}
//...
    1. You can pass in the middleware parameter directly in the route:

src/routes/user.ts

import * as Koa from 'koa';
import { get , post} from '../util/route-decors';

export default class User {
    @get('/list',{ 
        prefix:'/user',
        middlewares:[
            async function valitation(ctx:Koa.Context,next:()=> Promise<any>){
                // Verify whether there is a name field in the parameter
                const { name } = ctx.query;
                if(!name) throw "unkonw name property";
                await next();
            }
        ]
    })
    public pageList(ctx:Koa.Context){
        ctx.body = {
            state:"success",
            message:"Request succeeded~",
            data:{}
        }
    }
}
Routing guard (authentication)

In general, we need to authenticate some APIs during development, and we can also use decorators to complete functions;

src/util/route-decors.ts

//...
const decorate = (method: HTTPMethod, path: string, options: RouteOptions = {}, router: KoaRouter) => {
    return (target, property: string) => {
        // nexttick is used here because the method decorator executes before the class decorator
        process.nextTick(()=>{
            const middlewares = [];
            // If there is a decorator on the class, put the decorator on the class
            if(target.middlewares){
                middlewares.push(...target.middlewares);
            }

            // If there's a message in the decorator
            if(options.middlewares){
                middlewares.push(...options.middlewares);
            }
    
            const url = options.prefix ? options.prefix + path : path
            // You also need to put the normal business middleware into it
            middlewares.push(target[property]);
            router[method](url, ...middlewares);
        })
    }
}
export const middlewares = function middlewares(middlewares:Koa.Middleware[]) {
    return function (target) {
        target.prototype.middlewares = middlewares;
    };
};

src/routes/user.ts

//...
@middlewares([
    async function guard(ctx: Koa.Context, next: () => Promise<any>) {
        if (ctx.header.token) {
            await next();
        } else {
            throw "Please login";
        }
    }
])
export default class User {}
Database integration

Sequential typescript uses documents

Installation dependency:

$  npm install -S sequelize sequelize-typescript reflect-metadata mysql2 @types/bluebird @types/node @types/validator

index.ts

//...
import { Sequelize } from 'sequelize-typescript';

const database = new Sequelize({
    port:3306,
    database:'test',
    username:"root",
    password:'root',
    dialect:'mysql',
    // After adding this, it will automatically traverse all the files under the model directory and create the model
    modelPaths:[`${__dirname}/model`]
})
database.sync({force:true});

Create model: create a new model / user in src directory ts:

import { Table, Column, Model, DataType } from 'sequelize-typescript';

@Table({modelName : 'users'})
export default class User extends Model<User>{
    @Column({
        primaryKey: true,
        autoIncrement: true,
        type: DataType.INTEGER
    })
    public id:number;

    @Column(DataType.CHAR)
    public name: string;
}

Usage model: routes / user ts:

import model from '../model/user';

export default class User {
    @get('/users')
    public async list(ctx: Koa.Context) {
        const users = await model.findAll()
        ctx.body = { ok: 1, data: users };
    }
}

Typescript middle platform development open source library

ts type file definition

When developing ts, we sometimes encounter libraries without d.ts files. At the same time, when migrating from old projects to TS projects, we also encounter some files that need to write their own declaration files. However, when there are many declaration files needed, we need to automatically produce declaration files;

    1. Add a declaration file for the entire package; Using Microsoft DTS Gen
npm install -g dts-gen   # Install DTS Gen globally first
npm install -g yargs     # Then install the libraries you need to produce the declaration files globally
dts-gen -m yargs         # Execute the command to generate the file
    1. Generate a declaration file for a single file; Using dtsmake
npm i dtsmake -g   # Install dtsmake globally first
dtsmake -s /path/name.js  # File address to be generated

The generated files generally have some problems and need to be modified slightly. If you don't want to write the type, use any directly; An error may be reported during execution. If tern is not installed as required, you need to install it in the project directory NPM I tern -- save dev;

. d.ts writing

Some commonly used description files (. d.ts) can be directly downloaded using npm, such as jquery's, npm install @types/jquery; However, there are still some libraries or js codes used internally in development that need to be described manually. Sometimes, the above generation tools can not completely and correctly generate the description files of js codes;

. d.ts will also enhance the code prompt in the actual development process; For example, after we install the description file of jquery;

Global type

In the d.ts file, the keyword declare should be added in front of variables, functions or classes declared at the outermost layer In the rules of typescript, if one ts,. d. If the TS file does not use import or export syntax, the variables declared at the top level are global variables;

    1. Variable or constant
// variable
declare var a:number | string;

// constant
declare const PI:3.14;
    1. function
// No parameter no return value
declare function getName():void;

// function overloading
declare function getName(name:string):string;
declare function getName(id:number,name:string):string;

// Parameters are optional
declare function render(callback?:()=>void): string;
    1. class
declare class Person{
    static maxAge:number;
    constructor(name:string,age:number);
    getName(id:number):string;
}

constructor represents the construction method. Static is a static attribute and can only be accessed through classes, such as person maxAge;

    1. object
declare namespace fgui{
    
}

Of course, there may be attributes, methods, classes, etc. on this object. In fact, the above writing method is put in the namespace keyword, but the declare keyword is not required;

declare namespace app{
    namespace config{
        var baseUrl:string;
        var host:string;
        var port:string;
    }
    function getDeviceType():string;
    class Config{
        constructor();
    }
}

Mixed type

Sometimes some values are complex objects that are both functions and class objects For example, our commonly used jquery has various usages;

new $()
$.ajax()
$()
    1. Both functions and objects
declare function $fn(s:string):void;
declare namespace $fn{
    var name:string;
}

. d.ts will merge namespace and function with the same name;

    1. It is both a function and a class
interface Animal{
    name:string;
    getName():string;
}
interface AnimalStatic{
    new (name:string):Animal;
    AnimalName():string;
    (w:number):number;
}
declare var Animal:AnimalStatic;

Use as a function:

Use as class:

Static properties of class:

Modularity (CommonJS)

In addition to the above methods, we sometimes introduce modular code through require();

declare module "abcd"{
    export var a:number;
    export function ajax():void;
    export namespace global{
        var url:string;
    }
}

In fact, a module is nested in the outer layer. The writing method in it is similar to the above definition. Replace declare with export;

Keywords: node.js Front-end TypeScript

Added by rjs34 on Sat, 25 Dec 2021 16:04:32 +0200