共计 19393 个字符,预计需要花费 49 分钟才能阅读完成。
概述
深刻了解 TypeScript
TypeScript
具备类型零碎, 且是JavaScript
的超集. 它能够编译成一般的JavaScript
代码.TypeScript
是一个比JavaScript
更Java
的Script
;
装置
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
数据类型
typescript
中为了使编写的代码更标准, 更有利于保护, 减少了类型校验;
- 布尔类型 (boolean)
// 布尔类型 boolean
var 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); // undefined
num = 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()); // 1
var 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);
// mongodb
var 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 权限判断
- 注解
装璜器类型
常见的装璜器有: 类装璜器、属性装璜器、办法装璜器、参数装璜器
装璜器的写法: 一般装璜器 (无奈传参)、装璜器工厂(可传参)
类装璜器
-
- 无参数装璜器
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
-
- 有参数装璜器(工厂模式)
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
办法装璜器
-
- 无参数装璜器
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
-
- 装璜器工厂(可传参)
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 -y
npm 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/**/*"]
}
我的项目根底代码
-
- 装置依赖:
npm i koa koa-static koa-body koa-xtime glob -S
-
- 在 src 目录上面新建
index.ts
:
- 在 src 目录上面新建
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
;
-
- 路由发现及注册, 在 src 目录上面创立
util/route-decors.ts
:
- 路由发现及注册, 在 src 目录上面创立
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;
}
-
- 创立路由, 在 src 下新建
routes/user.ts
:
- 创立路由, 在 src 下新建
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:{}}
}
}
-
- 应用, 将
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
参数, 能够利用中间件的机制实现对数据的校验;
-
- 首先对
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);
}
}
//...
-
- 间接在路由外面就能够传入
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 我的项目时也会遇到一些文件须要本人编写申明文件, 然而在须要的申明文件比拟多的状况, 就须要自动生产申明文件;
-
- 为整个包增加申明文件; 应用微软的
dts-gen
- 为整个包增加申明文件; 应用微软的
npm install -g dts-gen # 先全局装置 dts-gen
npm install -g yargs # 而后在全局装置你须要生产申明文件的库
dts-gen -m yargs # 执行命令生成文件
-
- 为单个文件生成申明文件; 应用
dtsmake
- 为单个文件生成申明文件; 应用
npm i dtsmake -g # 先全局装置 dtsmake
dtsmake -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
语法的话, 那么最顶层申明的变量就是全局变量;
-
- 变量 or 常量
// 变量
declare var a:number | string;
// 常量
declare const PI:3.14;
-
- 函数
// 无参数无返回值
declare function getName():void;
// 函数重载
declare function getName(name:string):string;
declare function getName(id:number,name:string):string;
// 参数可有可无
declare function render(callback?:()=>void): string;
-
- 类 class
declare class Person{
static maxAge:number;
constructor(name:string,age:number);
getName(id:number):string;
}
constructor
示意的是构造方法, static
为动态属性, 只能通过类来拜访, 例如: Person.maxAge
;
-
- 对象
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()
$()
-
- 即是函数也是对象
declare function $fn(s:string):void;
declare namespace $fn{var name:string;}
.d.ts
会将同名的 namespace
和 function
合并到一起;
-
- 即是函数又是类
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
;