概述

深刻了解 TypeScript

TypeScript 具备类型零碎, 且是JavaScript的超集. 它能够编译成一般的 JavaScript代码. TypeScript 是一个比 JavaScriptJavaScript;

装置

npm install typescript -gtsc#   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

数据类型

typescript 中为了使编写的代码更标准, 更有利于保护, 减少了类型校验;

  • 布尔类型 (boolean)
//  布尔类型 booleanvar flag:boolean = true;
  • 数字类型 (number)
//  数字类型let num:number = 123;
  • 字符串类型 (string)
//  字符串类型let str:string = "aaa";
  • 数组类型 (array)
/* *  数组类型有两种形式 */// 指定数组类型为 number 类型var arr:number[] = [];// 泛型的形式定义数组, 指定数组元素为 number 类型var arr:Array<number> = [];
  • 元组类型 (tuple)
//  元组类型, 也是数组的一种, var arr:[string,number,boolean] = ["",123,true];
  • 枚举类型 (enum)
//  枚举类型  enum 默认状况下键值从 0 开始; 如果不给属性赋值就从 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)
//  任意类型, 能够反复批改值类型var a:any = 123;a = "hello";a = false;
  • null 和 undefined
//  默认状况下 null 和 undefined 是所有类型的子类型;var num:number | undefined;console.log(num);       // undefinednum = 123;
  • void 类型
//  示意没有任何类型, 罕用于函数的返回值类型function print(str:string):void{    console.log(str);}print("hello");

类型断言

类型断言和其余强类型语言外面的类型转换一样, 然而不进行非凡的数据检查和解构;

// str 为 any 类型;var str:any = "this is a string";// 把 any 类型的 str 转换为 string 类型, 取 string 的 length;let len:number = (<string>str).length;

下面的写法也能够应用 as 语法:

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

es5 外面类的定义和继承

回顾一下 es5 外面类的定义;

// 类的构造函数function Person(){    // 类的属性    this.name = "xxx";    this.age = 24;    // 类的办法    this.run = function(){        alert(this.name + "在静止~");    }}// 还能够通过原型链的形式增加类的属性和办法Person.prototype.sex = "male";Person.prototype.working = function(){    alert(this.name + "在工作~");}// 类的动态属性Person.getInfo = function(){    console.log("这是类的动态属性~");}var p = new Person();p.run();Person.getInfo();// 类的继承   原型链和对象假冒的形式继承function Student(){     // 对象假冒实现继承, 对象假冒的形式只能继承自构造函数内定义的属性和办法, 没方法继承原型链下面的属性和办法;    Person.call(this);  }var w = new Student();w.run();w.working();            //  error// 原型链的形式继承, 原型链能够继承构造函数外面的属性和办法, 也能够继承原型链下面的属性和办法; // 然而如果父类的构造函数须要传递参数的时候, 实例化子类的时候就没方法给父类传参数Student.prototype = new Person();

组合形式实现类的继承

function Person(name, age){    this.name = name;    this.age = age;    this.run = function(){        alert(this.name + "在静止~");    }}Person.prototype.sex = "Male";Person.prototype.working = function(){    alert(this.name + "在工作~");}Person.getInfo = function(){    alert("这是类的静态方法~");}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();

typescript 类的定义和继承

typescript 中类的定义;

class Person{    name:string;    age:number;    constructor(name:string,age:number){        this.name = name;        this.age = age;    }    run():void{        alert(this.name + "在静止~");    }    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 + "在工作~");    }}var s:Student = new Student("Kangkang",25);s.run();s.setName("Hope");

类修饰符

typescript 外面定义属性的时候, 给咱们提供了拜访修饰符;

  • public : 私有的, 在类外面, 子类, 类里面都能够拜访到;
  • protected : 爱护类型, 在类外面, 子类外面能够拜访, 在类内部不能拜访;
  • private : 公有的, 只能在以后类外部拜访;

如果不加任何修饰符, 默认为 public 拜访级别;

class Person{    private name:string;    private age:number;    //  `static` 关键字, 能够将类外面的属性和办法定义为类的动态属性和办法    public static sex:string = "Male";    constructor(name:string,age:number){        this.name = name;        this.age = age;    }    public run():void{        alert(this.name + "在静止~");    }    public setName(name:string):void{        this.name = name;    }}var p:Person = new Person("Tonny",22);console.log(Person.sex);

抽象类

abstract 关键字定义的抽象类, 这个类要求继承它的子类必须实现抽象类的形象办法;

abstract class Animal{    abstract eat():void;}class Dog extends Animal{    constructor(){        super();    }    eat():void{            }}

抽象类作为其余派生类的基类应用, 语法有点相似接口办法, 两者都是定义方法签名但不蕴含办法体, 例如下面的 Animal 抽象类, 定义了 eat 形象办法, 继承自它的子类必须实现 eat 办法;

接口

在面向对象的程序设计中, 接口是一种标准的定义, 它定义了行为和动作的标准, 在程序设计中, 接口起到一种限度和标准作用, 接口定义了某一批类所须要恪守的标准, 接口不关怀这些类的外部状态数据, 也不关怀这些类里办法的实现细节, 它只规定这批类里必须提供某些办法, 提供这些办法的类就能够满足理论须要;

typescript 中的接口相似 java, 同时还减少了更灵便的接口类型, 蕴含属性, 函数, 可索引和类等.

属性接口

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 关键字能够对函数参数进行束缚, 下面的代码中咱们须要 printName 函数接管一个蕴含 firstName, secondName 的 json 对象; 如果传入的参数不蕴含这两个字段程序就会提醒谬误;

另外, 能够看到 age? 为可选参数, 不是非必须传入的;

函数类型接口

函数类型接口: 对办法传入的参数以及返回值进行束缚;

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"));

可索引接口

可索引接口是对数组和对象进行束缚的接口;

// 对数组的束缚var arr1:number[] = [1,2,3,5];var arr2:Array<string> = ['1','hahe','akon'];interface UserArray{    // 数组的索引为 number 类型, 索引值为 string 类型    [index:number]:string}var arr:UserArray = ["jiakang","kongkon"];var arr:UserArray = [12,56];            // 报错

类类型接口

对类的束缚, 和抽象类有点相似;

interface Animal{    name:string,    eat(str:string):void,}// 类须要实现类接口class Dog implements Animal{    name:string;    constructor(name:string){        this.name = name;    }    eat():void{    }}var d:Dog = new Dog("xiaogou");

implements 关键字实用于类来实现接口的标准; 如上代码, Dog 类实现了 Animal 接口的定义;

泛型

在开发中, 咱们不仅要思考统一的定义良好的 api, 同时也要思考可重用性; 组件不仅可能反对以后的数据类型, 同时也能反对将来的数据类型, 这在开发大型的零碎时候能够提供非常灵便的性能;

艰深的了解泛型, 就是解决 类, 接口, 办法的复用性, 以及对不特定数据的反对;

function GetData<T>(value:T):T{    return value;}GetData<number>(123);GetData<string>("aaa");

泛型函数要求在函数调用的时候指定类型, 下面的 GetData 函数示意传入的参数类型与返回的参数类型统一;

泛型类

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

下面的 MinxNum 类能够取得传入参数中的最小值, 然而有一个缺点就是只能返回 number 类型; 如果咱们还须要 a-z 字符之间返回最小字符就须要从新批改一下这个工具类;

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());       // 1var n = new MinxNum<string>();n.add("n");n.add("a");n.add("d");console.log(n.min());       // a

泛型函数

interface Config<T>{    (value:T):T;}function GetData<T>(value:T):T{    return value;}var d:Config<string> = GetData;d("aaa");

泛型类束缚

泛型能够帮忙咱们防止反复的代码以及对不特定的数据类型的反对(类型校验), 咱们能够把类当做参数的泛型类;

  • 定义一个类
  • 把类作为参数来束缚数据传入的类型
/** *  定义一个 user 类, 这个类的作用就是映射数据库的表字段 *  定义一个 Mysql 类, 这个类用来操作数据库 *  把 user 类作为参数传入到 Mysql 中 */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);

如此来如果咱们还须要减少一个文章的类, 并且也须要将文章插入到数据库中, 这样 Mysql 类就不能将 User 类作为参数传递进去了; 能够应用泛型, 将 Mysql 类做成一个通用的工具类, 来对数据库进行操作;

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 = "新闻大爆炸";art.desc = "这是一条爆炸性的新闻";art.status = 0;var db:Mysql<User> = new Mysql<User>();db.add(u);var artc:Mysql<ArticleCate> = new Mysql<ArticleCate>();artc.add(art);

Typescript 封装 DB

  • 性能: 定义一个操作数据库的类, 反对 Mysql, MongoDb;
  • 要求: 性能一样, 都有 add, delete, update, find 办法;
  • 留神: 代码的重用性和束缚对立的标准
  • 思路: 须要束缚标准, 所以要定义接口, 须要代码重用, 所以须要应用泛型
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 [];    }}// 定义一个 User 类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);// mongodbvar oMongo:MongoDB<User> = new MongoDB<User>();oMongo.add(u);

命名空间

在代码量较大的状况下, 为了防止各种变量名抵触, 能够将类似的性能的函数, 类, 接口等都搁置在命名空间内;

Java 的包, .net 的命名空间一样, typescript 的命名空间能够将代码包裹起来, 只对外裸露须要在内部拜访的对象, 命名空间内的对象通过 export 关键字导出; 调用办法应用命名空间的命名调用;

命名空间和模块的区别: 命名空间属于外部模块, 次要用于组织代码, 避免代码命名抵触; 模块则并重代码的复用性, 一个模块外面可能会有多个命名空间;

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 [];        }    }        // 定义一个 User 类    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);

装璜器

装璜器容许向一个现有的对象增加新的性能, 同时又不扭转其构造, 这种类型的设计模式属于结构型模式, 它是作为一个现有的类的包装;

这种模式创立了一个装璜类, 用来包装原有的类, 并在放弃类办法签名的完整性的前提下, 提供了额定的性能;

装璜器原理

装璜器, 实质上就是一个函数, 它会在运行时被调用, 被装璜的申明信息做为参数传入; 实践上疏忽参数的话, 任何函数都能够当做装璜器应用;

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 }    }}

装璜器实用场景

装璜器可能被附加到类申明, 办法, 属性或参数上, 能够批改类的行为;

  • AOP 切面利用:

    • 动态注入形式
    • 反射机制
  • 凋谢关闭准则, 类的解耦:

    • MVC 权限判断
    • 注解

装璜器类型

常见的装璜器有: 类装璜器、属性装璜器、办法装璜器、参数装璜器

装璜器的写法: 一般装璜器 (无奈传参) 、 装璜器工厂(可传参)

类装璜器
    1. 无参数装璜器
function logClass(category:any){    console.log('category',category);    category.prototype.apiUrl = 'http://www.baidu.com';    category.prototype.func = function(){};}@logClassclass HttpClient{    constructor(){}}const http = new HttpClient();console.log(http['apiUrl']);//  category [Function: HttpClient]//  http://www.baidu.com
    1. 有参数装璜器(工厂模式)
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
办法装璜器
    1. 无参数装璜器
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. 装璜器工厂(可传参)
function fnMethod(params){    return (target, property, descriptor)=>{        // 保留一下旧的办法        const oMthod = descriptor.value;        // 改写旧的办法        descriptor.value = function(a,b){            // 调用未改写的办法            //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
属性装璜器

属性装璜器表达式会在运行时当作函数被调用, 传入下列2个参数:

  • 对于动态成员来说是类的构造函数, 对于实例成员是类的原型对象;
  • 属性名;
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

装璜器执行程序

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] */

执行程序: 属性 --> 办法 --> 类

Typescript 装璜器实际

NodeJs + Typescript + Koa 装璜器实现后盾 api 开发;

我的项目初始化
npm init -ynpm install typescript ts-node-dev tslint @types/node -D

批改 package.json 为:

"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"}

在根目录下创立 tsconfig.json:

{    "compilerOptions": {        "outDir": "./dist",        "target": "es2017",        "module": "commonjs",                   //组织代码形式        "sourceMap": true,        "moduleResolution": "node",             // 模块解决策略        "experimentalDecorators": true,         // 开启装璜器定义        "allowSyntheticDefaultImports": true,   // 容许es6形式import        "lib": [            "es2015"        ],        "typeRoots": [            "./node_modules/@types"        ],    },    "include": [        "src/**/*"    ]}
我的项目根底代码
    1. 装置依赖:
npm i koa koa-static koa-body koa-xtime glob -S
    1. 在 src 目录上面新建 index.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("服务器启动胜利~");});
npm start
实现路由定义和发现

需要: 定义一个装璜器, 实现 router 的主动注册;
要求: @get 的时候, 路由的申请形式为 get, @post 路由的申请形式为 post; 当传进去 prefix, 对用的路由规定为 prefix/xxx, 例如:api/users;

    1. 路由发现及注册, 在 src 目录上面创立 util/route-decors.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 = {    /**    * 路由文件扩展名,默认值是`.{js,ts}`    */    extname?: string;};type RouteOptions = {    /**    * 实用于某个申请比拟非凡,须要独自制订前缀的情景    */    prefix?: string;    /**    * 给以后路由增加一个或多个中间件    */    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. 创立路由, 在 src 下新建 routes/user.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:"申请胜利~",            data:{}        }    }}
    1. 应用, 将 routes 目录上面的所有的路由用 loader 读取进去, 并注册到 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("服务器启动胜利~");});
npm start# http://127.0.0.1:3001/user/list
数据校验

下面咱们对路由的装璜器保留了一个 middlewares 参数, 能够利用中间件的机制实现对数据的校验;

    1. 首先对 route-decors进行革新:
//...const decorate = (method: HTTPMethod, path: string, options: RouteOptions = {}, router: KoaRouter) => {    return (target, property: string) => {        const middlewares = [];        // 如果装璜器外面有传过来中间件        if(options.middlewares){            middlewares.push(...options.middlewares);        }                const url = options.prefix ? options.prefix + path : path        // 还须要将失常业务的中间件放进去        middlewares.push(target[property]);        router[method](url, ...middlewares);    }}//...
    1. 间接在路由外面就能够传入 middleware 参数了:

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>){                // 校验参数外面是否有 name 字段                const { name } = ctx.query;                if(!name) throw "unkonw name property";                await next();            }        ]    })    public pageList(ctx:Koa.Context){        ctx.body = {            state:"success",            message:"申请胜利~",            data:{}        }    }}
路由守卫(鉴权)

通常开发状况下, 咱们须要对某些 api 进行鉴权, 也能够应用装璜器来实现性能;

src/util/route-decors.ts

//...const decorate = (method: HTTPMethod, path: string, options: RouteOptions = {}, router: KoaRouter) => {    return (target, property: string) => {        // 这里应用 nexttick 是因为办法装璜器比类装璜器先执行        process.nextTick(()=>{            const middlewares = [];            // 如果类下面有装璜器就把 类的装璜器也放进去            if(target.middlewares){                middlewares.push(...target.middlewares);            }            // 如果装璜器外面有传过来中间件            if(options.middlewares){                middlewares.push(...options.middlewares);            }                const url = options.prefix ? options.prefix + path : path            // 还须要将失常业务的中间件放进去            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 "请登录";        }    }])export default class User {}
数据库整合

sequelize-typescript 应用文档

装置依赖:

$  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',    // 增加这个之后会主动遍历 model 目录上面的所有文件, 并创立模型    modelPaths:[`${__dirname}/model`]})database.sync({force:true});

创立模型: src 目录下新建 model/user.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;}

应用模型: 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 中台开发开源库

ts 类型文件定义

在开发ts时, 有时会遇到没有 d.ts 文件的库, 同时在老我的项目迁徙到ts我的项目时也会遇到一些文件须要本人编写申明文件, 然而在须要的申明文件比拟多的状况, 就须要自动生产申明文件;

    1. 为整个包增加申明文件; 应用微软的 dts-gen
npm install -g dts-gen   # 先全局装置dts-gennpm install -g yargs     # 而后在全局装置你须要生产申明文件的库dts-gen -m yargs         # 执行命令生成文件
    1. 为单个文件生成申明文件; 应用 dtsmake
npm i dtsmake -g   # 先全局装置dtsmakedtsmake -s /path/name.js  # 须要生成的文件地址
生成的文件个别都会有一些问题, 须要本人略微批改一下, 如果不想写类型间接用 any; 执行的时候可能会报错 tern 没有按装, 就须要在装置一下, 在我的项目目录 npm i tern --save-dev;

.d.ts 编写

一些罕用的形容文件(.d.ts) 都能够间接应用 npm 下载, 例如 jquery 的, npm install @types/jquery; 但还是有一些开发中外部应用的库或者是 js 代码须要手动去形容文件, 下面的生成工具有时候并不能完全正确的生成 js 代码的形容文件;

.d.ts 在理论开发过程中也会加强代码提醒; 例如, 当咱们装置了 jquery 的形容文件之后;

全局类型

d.ts 文件外面, 在最外层申明变量或者函数或者类要在后面加上 declare 这个关键字. 在 typescript 的规定外面, 如果一个 .ts.d.ts文件如果没有用到 import 或者 export 语法的话, 那么最顶层申明的变量就是全局变量;

    1. 变量 or 常量
// 变量declare var a:number | string;// 常量declare const PI:3.14;
    1. 函数
// 无参数无返回值declare function getName():void;// 函数重载declare function getName(name:string):string;declare function getName(id:number,name:string):string;// 参数可有可无declare function render(callback?:()=>void): string;
    1. 类 class
declare class Person{    static maxAge:number;    constructor(name:string,age:number);    getName(id:number):string;}

constructor 示意的是构造方法, static 为动态属性, 只能通过类来拜访, 例如: Person.maxAge;

    1. 对象
declare namespace fgui{    }

当然, 这个对象下面可能有属性, 办法, 类等, 其实就是把下面的写法放在了 namespace 关键字外面, 然而不须要 declare 关键字了;

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

混合类型

有时候有些值既是函数又是 class 又是对象的简单对象. 比方咱们罕用的 jquery 有各种用法;

new $()$.ajax()$()
    1. 即是函数也是对象
declare function $fn(s:string):void;declare namespace $fn{    var name:string;}

.d.ts 会将同名的 namespacefunction 合并到一起;

    1. 即是函数又是类
interface Animal{    name:string;    getName():string;}interface AnimalStatic{    new (name:string):Animal;    AnimalName():string;    (w:number):number;}declare var Animal:AnimalStatic;

作为函数应用:

作为类应用:

类的动态属性:

模块化(CommonJS)

除了下面的形式, 咱们有时候还是通过 require() 的形式引入模块化的代码;

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

其实就是在外层嵌套了一个 module, 外面的写法和下面的定义差不多, 把 declare 换成了 export;