# Typescript 笔记

# 一、安装 编译

在使用 npm 命令前电脑必须得安装 nodejs

安装

npm install -g typescript
或者
cnpm install -g typescript
或者
yarn global add typescript

注意 如果电脑上没有安装过 cnpm,请先安装 cnpm

npm install -g cnpm --registry=https://registry.npm.taobao.org

注意 如果电脑上没有安装过 yarn,请先安装 yarn

npm install -g yarn
或者
cnpm install -g yarn

运行

tsc filename.ts

自动转译

tsc --init

生成 tsconfig.json 配置文件,修改配置

"outDir":"./js"
//js 生成目录

vscode 需要点击选项卡的 “终端”,点击 “运行任务”,点击监视所需要监视的 ts 文件夹或者项目即可。

# 二、数据类型

typescript 为了使编写的代码更加规范,更有利于维护

# 1、布尔类型 (boolean)

var flag:boolean = true;
flag = false;

# 2、数字类型 (number)

数字类型包括整型和浮点型

var a:number = 123;
a="string";	// 错误写法
a = 3;	// 正确写法

# 3、字符串类型 (string)

var str:string = 'hello world';
str = 'hello typescript';	// 正确写法

# 4、数组类型 (array)

typescript 中有多种定义数组的方法

// 第一种定义数组的方式
var arr:number[] = [1,2,3,4];
var str:string[] = ['php','js','golang'];
// 第二种定义数组的方法 PS:vue 似乎不支持泛型
let arr:Array<number> = [1,2,3,4,5];
let str:Array<string> = ['php','js','golang'];
// 第三种
var arr:any[] = ['ts',123,true];
console.log(arr);	//['ts',123,true];

# 5、元组类型 (tuple)

属于数组的一种,可以指定每一个元素的数据类型

let arr:[string,number,boolean] = ['ts','3.14',true];

# 6、枚举类型 (enum)

enum Flag{
    success=1,	// 标识符 [= 整型常数]
    error=-1
}
var f:Flag = Flag.success;
console.log(f);		//1
console.log(Flag.error);	//-1
enum Color{
    red,blue = 5,orange
}
var c:Color = Color.red;
console.log(c);	//0	如果没有写整型常数,默认下标
console.log(Color.orange);	//6 从最近的有值的开始放后推

# 7、任意类型 (any)

相当于 js 原来的样子 适用于类型会经常变化的任意类型或者不属于常用数据类型的类型

var num:any = 123;
num = 'str';
console.log(num);	//str

# 8、null 和 undefined

其他类型 (never) 的字类型

var num:number;
console.log(num);	//undefined	报错
var num:undefined;
console.log(num);	//undefined 正确
var num:number|undefined;
console.log(num);	//undefined 正确
num = 123;
console.log(123);	//123
// 定义未赋值就是 undefined
var num:null;
num = null;
// 一个元素可能是 number 可能是 null,可能是 undefined
var num:number | null | undefined;
num = 1234;
console.log(num);	//1234

# 9、void 类型

typescript 中的 void 表示没有任何类型,一般用于定义方法的时候没有返回值

function run():void{// 表示方法没有返回任何类型
	console.log('run');
}

# 10、never 类型

是其他类型 (包括 null 和 undefined) 的字类型,代表从不会出现的值

这意味着声明 never 的变量只能被 never 类型所赋值

var a:never;
a = 123;	// 错误的写法
let a:never;
a=(()=>{
    throw new Error('错误');
})

# 三、函数

# 1、函数的定义

以前 es5

function run(){
	return 'run';
}
// 匿名函数
let run = function(){
    return 'run';
}

typescript

function run():string{
	return 'run';
}
// 匿名函数
let run = function():string{
    return 'run';
}

传参

function getInfo(name:string,age:number):string{
	return `${name} ---- ${age}`;
}
console.log(getInfo('张三',21));
var getInfo = function(name:string,age:number):string{
    return `${name} --- ${age}`;
}
var getInfo = function(name:string,age:number):void{
    console.log(name,age);
}

# 2、可选参数

es5 里方法的实参和形参可以不一样,但是 ts 中必须一样,如果不一样就需要配置可选参数

function getInfo(name:string,age?:number):string{
	if(age){
		return `${name} --- ${age}`;
	}else{
        return `${name} --- 年龄保密`;
    }
}

可选参数必须是形参最后面

# 3、默认参数

function getInfo(name:string,age:number=20):string{
	return `${name} ---  ${age}`;
}
console.log(getInfo('张三'));	// 张三 --- 20

# 4、剩余参数

function sum(a:number,b:number,c:number,d:number):number{
    return a+b+c+d;
}
let num = sum(1,2,3,4);
console.log(num);	//10
function sum(...result:number[]):number{
    let sum = 0;
    for(let i = 0;i < result.length;i++){
        sum += result[i];
    }
    return sum;
}
let num = sum(1,2,3,4);
console.log(num);	//10
function sum(a:number,...result:number[]):number{
	var sum = a;
	for(let i = 0;i < result.length;i++){
		sum += result[i];
	}
	return sum;
}

console.log(sum(1,2,3,4,5,6,7));

# 5、函数重载

通过为同一个函数提供多个函数类型定义来试下多种功能的目的

function getInfo(name:string):string{
    
}
function getInfo(age:number):string{
    
}
function getInfo(str:any):any{
    if(typeof str === 'string'){
        return '我叫' + str;
    }else{
        return '我的年龄是' + str;
    }
}
console.log(getInfo('张三'));// 我叫张三
console.log(getInfo(21));// 我的年龄是 21

# 6、箭头函数

setTimeOut(()=>{
    
},500);

# 四、类

# 1、es5 中的类

# 1.1 定义类

es5 里的类就是构造函数

function Person(){
	this.name = '张三';
	this.age = 20;
}
var p = new Person();
console.log(p.name);	// 张三

构造函数里增加方法

function Person(){
	this.name = '张三';
	this.age = 20;
	this.run = function(){
		console.log(this.name + '在运动');
	}
}
var p = new Person();
// 原型链上的属性会被多个实例共享,但是构造函数不会
Person.prototype.sex = '男';
Person.prototype.work = function(){
    console.log(this.name + '在工作');
}
console.log(p.name);	// 张三
p.run();	// 张三在运动
p.work();	// 张三在工作

类里的静态方法

function Person(){
	this.name = '张三';
	this.age = 20;
	this.run = function(){
		console.log(this.name + '在运动');
	}
}
// 静态方法
Person.getInfo = function(){
    console.log('我是静态方法');
}
// 通过 new 实例才能调用的方法不是静态方法
var p = new Person();
// 调用静态方法
Person.getInfo();

# 1.2 继承

es5 里的继承

// 对象冒充继承 -------------------------
function Person(){
	this.name = '张三';
	this.age = 20;
	this.run = function(){
		console.log(this.name + '在运动');
	}
}
Person.prototype.sex = '男';
Person.prototype.work = function(){
    console.log(this.name + '在工作');
}
//web 类 继承 person 类 原型链 + 对象冒充的组合继承模式
function Web(){
    Person.call(this);	// 对象冒充实现继承
}
var w = new Web();
w.run();	// 张三在运动
w.work();	// 报错 对象冒充可以继承属性和方法,但是没办法继承原型链上的属性和方法
// 原型链上的继承 ------------------------------
function Person(){
	this.name = '张三';
	this.age = 20;
	this.run = function(){
		console.log(this.name + '在运动');
	}
}
Person.prototype.sex = '男';
Person.prototype.work = function(){
    console.log(this.name + '在工作');
}
function Web(){
    
}
// 继承了构造函数里的属性和方法以及原型链上的属性和方法
web.prototype = new Person();
// 原型链继承的问题 ---------------------
function Person(name,age){
	this.name = name;
	this.age = age;
	this.run = function(){
		console.log(this.name + '在运动');
	}
}
Person.prototype.sex = '男';
Person.prototype.work = function(){
    console.log(this.name + '在工作');
}
var p = new Person('李四',21);
p.run();	// 李四在运动
function Web(name,age){
    
}
web.prototype = new Person();
var w = new Web('赵四',20);	// 实例化子类的时候没法给父类传参
w.run();	//undefined 在运动
// 原型链 + 构造函数的组合继承模式 ------------------------
function Person(name,age){
	this.name = name;
	this.age = age;
	this.run = function(){
		console.log(this.name + '在运动');
	}
}
Person.prototype.sex = '男';
Person.prototype.work = function(){
    console.log(this.name + '在工作');
}
function Web(name,age){
    Person.call(this,name,age);	// 对象冒充 实例化子类可以给父类传参
}
Web.prototype = new Person();
var w = new Web('赵四',20);
w.run();	// 赵四在运动
w.work();	// 赵四在工作
// 原型链 + 对象冒充的另一种继承方式 ---------------------
function Person(name,age){
	this.name = name;
	this.age = age;
	this.run = function(){
		console.log(this.name + '在运动');
	}
}
Person.prototype.sex = '男';
Person.prototype.work = function(){
    console.log(this.name + '在工作');
}
function Web(name,age){
    Person.call(this,name,age);	// 对象冒充 实例化子类可以给父类传参
}
Web.prototype = Person.prototype;
var w = new Web('赵四',20);
w.run();	// 赵四在运动
w.work();	// 赵四在工作

# 2、Typescript 中的类

# 2.1 定义类

class Person{
	name:string;	// 属性 前面省略了 public 关键词
	
	constructor(name:string){	// 构造函数 实例化类的时候触发的方法
		this.name = name;
	}
	run():void{
        console.log(this.name + '在运动');
    }
}
var p = new Person('张三');
p.run();	// 张三在运动
class Person{
	name:string;	// 属性 前面省略了 public 关键词
	
	constructor(name:string){	// 构造函数 实例化类的时候触发的方法
		this.name = name;
	}
    
    getName():string{
        return this.name;
    }
    setName(name:string):void{
        this.name = name;
    }
	run():void{
        console.log(this.name + '在运动');
    }
}
var p = new Person('张三');
p.run();	// 张三在运动
console.log(p.getName());	// 张三
p.setName('李四');
console.log(p.getName());   // 李四

# 2.2 继承

extends/super

class Person{
    name:string;
    constructor(name:string){
        this.name = name;
    }
    run():string{
        return this.name + '在运动';
    }
}
var p = new Person('张三');
console.log(p.run());   // 张三在运动
class Web extends Person{
    constructor(name:string){
        super(name);
    }
}
let w = new Web('李四');
console.log(w.run());   // 李四在运动

子类在继承父类之后,还可以自己拓展新的方法

class Person {
    name: string;
    constructor(name: string) {
        this.name = name;
    }
    run(): string {
        return this.name + '在运动';
    }
}
var p = new Person('张三');
console.log(p.run());   // 张三在运动
class Web extends Person {
    constructor(name: string) {
        super(name);
    }
    work():string{
        return this.name + '在工作';
    }
}
let w = new Web('李四');
console.log(w.run());   // 李四在运动
console.log(w.work());  // 李四在工作

如果子类定义的方法在父类中已存在,执行子类方法

class Web extends Person {
    constructor(name: string) {
        super(name);
    }
    work():string{
        return this.name + '在工作';
    }
    run(){
        return '子类' + this.name + '在运动';
    }
}
let w = new Web('李四');
console.log(w.run());   // 子类李四在运动
console.log(w.work());  // 李四在工作

# 2.3 类中的修饰符

typescript 里面定义属性的时候给我们提供了三种修饰符

# public

公有 在类里面 子类 类外面都可以访问

class Person {
    public name: string;
    constructor(name: string) {
        this.name = name;
    }
    run(): string {
        return this.name + '在运动';
    }
}
// 类外部访问共有属性
let p = new Person('哈哈哈');
console.log(p.name);	// 哈哈哈
# protected

保护类型 在类里面和子类可以访问 类外面无法访问

class Person {
    protected name: string;
    constructor(name: string) {
        this.name = name;
    }
    run(): string {
        return this.name + '在运动';
    }
}
let p = new Person('张三');
console.log(p.name);	// 张三 但是语法不允许
# private

私有 在类里面可以访问,子类,类外部都没法访问

class Person {
    private name: string;
    constructor(name: string) {
        this.name = name;
    }
    run(): string {
        return this.name + '在运动';
    }
}
class Web extends Person{
    constructor(name:string){
        this.name = name;		
    }
    work():string{
        return this.name + "在运动";// 这里就会报错 编译不通过 只能在 Person 里访问
    }
}

# 2.4 静态属性和静态方法

直接用类名调用

静态方法里无法调用非静态属性

class Person{
    public name:string;
    static age:number = 20;
    constructor(name:string){
        this.name = name;
    }
    // 实例方法 实例化之后才能使用的方法
    run():void{
        console.log(this.name + '在运动');
    }
    work():void{
        console.log(this.name + '在工作');
    }
    static print(){
        console.log('静态方法' + this.age);
    }
}
let p = new Person('张三');
p.work();      // 张三在工作
Person.print(); // 静态方法 20
console.log(Person.age);    //20

# 2.5 多态

父类定义一个方法不去实现 让继承他的子类去实现 每一个子类有不同的表现

多态属于继承

class Animal{
    name:string;
    constructor(name:string){
        this.name = name;
    }
    eat():void{ // 具体吃什么不知道 继承他的子类去实现 每一个子类的体现不一样
        console.log('吃');
    }
}
class Dog extends Animal{
    constructor(name:string){
        super(name);
    }
    eat(): string {
        return this.name + '吃骨头';
    }
}
class Cat extends Animal{
    constructor(name:string){
        super(name);
    }
    eat(): string {
        return this.name + '吃猫粮';
    }
}
let dog = new Dog('小狗');
let cat = new Cat('小猫');
console.log(dog.eat()); // 小狗吃骨头
console.log(cat.eat()); // 小猫吃猫粮

# 2.6 抽象类和抽象方法

typescript 中的抽象类 他是提供其他类的基类,不能直接被实例化。

用 abstract 关键字定义抽象类和抽象方法,抽象类中的抽象方法不包含具体实现并且必须在派生类中实现

abstract 抽象方法只能放在抽象类中

抽象类和抽象方法用来定义标准

抽象方法不包含具体实现,并且必须在派生类中实现

Animal 要求她的子类必须包含 eat 方法

abstract class Animal{
    abstract eat():any;
    name:string;
    constructor(name:string){
        this.name = name;
    }
}
class Dog extends Animal{
    // 抽象类的子类必须实现抽象类里面的方法
    constructor(name:string){
        super(name);
    }
    eat() {
        console.log(this.name + '吃骨头');
    }
}
let dog = new Dog('小狗');
dog.eat();  // 小狗吃骨头
class Cat extends Animal{
    constructor(name:string){
        super(name);
    }
    catch():void{
        console.log(this.name + '抓老鼠');
    }
    eat() {
        console.log(this.name + '吃鱼');
    }
}
let cat = new Cat('小猫');
cat.eat();  // 小猫吃鱼

# 五、接口

接口的作用,在面向对象的编程中,接口是一种规范的定义,它定义了行为和动作的规范,在程序设计里面,接口起到了一种限制和规范的作用,接口定义了某一批类所需要遵守的规范,接口口不关心这些类内部的状态数据,也不关心这些类里方法的实现细节,他只规定这批类里必须提供某些方法,提供这些方法的类就可以满足实际需要。typescript 中的接口类似于 java,同时还增加了更灵活的接口类型,包括属性、函数、可索引和类等。

# 1、属性接口

//ts 中定义方法
function printLabel():void{
    console.log('printLabel');
}
printLabel();
// 参数进行约束
function printLabel(label:string):void{
    console.log('pringLabel');
}
printLabel('ts');
//ts 这种自定义方法传入参数对 json 进行约束
function printLabel(labelInfo:{'label':string}):void{
    console.log('printLabel');
}
let labelJson = {
    label:'ts'
}
printLabel(labelJson);

接口:行为和动作的规范 对批量方法进行约束

// 就是传入对象的约束  属性接口
interface FullName{
    firstName:string;
    secondName:string;
}
function printName(name:FullName){
    // 必须传入对象 firstName secondName
    console.log(name.firstName + '--' + name.secondName);
}
let obj = {
    firstName:'liu',
    secondName:'xin',
    age:21
}
printName(obj);
//------- 这种写法只能有符合要求的对象属性
printName({firstName:'liu',secondName:'xin'});
// 对批量方法进行约束
interface FullName{
    firstName:string;
    secondName:string;
}
function printName(name:FullName){
    // 必须传入对象 firstName secondName
    console.log(name.firstName + '--' + name.secondName);
}
function printInfo(info:FullName){
    console.log(info.firstName + info.secondName + info.age);// 这里的 age 不符合规范,编译会报错
}
let obj = {
    firstName:'liu',
    secondName:'xin',
    age:21
}
let info = {
    firstName:'liu',
    secondName:'xin',
    age:21
}
printName(obj);
printInfo(info);

# 2、接口可选属性

// 接口可选属性
interface FullName{
    firstName:string;
    secondName?:string;
}
function getName(name:FullName){
    console.log(name);
    
}
// 顺序没关系,类型和内容必须符合
getName({firstName:'liu'});
// 示例 -------------------------
interface Config{
    type:string;
    url:string;
    data?:string;
    dataType:string
}
function ajax(config:Config):void{
    let xht = new XMLHttpRequest();
    xht.open(config.type,config.url,true);
    xht.send(config.data);
    xht.onreadystatechange = function(){   
        if(xht.readyState == 4 && xht.status == 200){
            if(config.dataType == 'json'){
                console.log(JSON.parse(xht.responseText));
            }else{
                console.log(xht.responseText);
            }
        }
    }
}
ajax({
    type:'get',
    url:'http://127.0.0.1:2001/restaurant/food/getClassList',
    dataType:'json',
    // data:'name=zhangsan'
})

# 3、函数类型接口

对方法传入的参数以及返回值进行约束

加密的函数类型接口

interface encrypt{
    (key:string,value:string):string;
}
let md5:encrypt = function(key:string,value:string):string{
    // 模拟操作
    return key+value;
}
console.log(md5('name','zhangsan'));	//namezhangsan

# 4、可索引接口

数组、对象的约束(不常用)

// 第一种 数组
interface UserArr{
    [index:number]:string;
}
let arr:UserArr = ['1','2','3'];
console.log(arr[0]);
interface UserArr{
    [index:number]:any;
}
// 第二种 对象
interface UserObj{
    [index:string]:string;
}
let obj:UserObj = {name:'20',age:'20'};

# 5、类类型接口

对类的约束和抽象类有点相似

interface Animal{
    name:string;
    eat(str:string):void;
}
class Dog implements Animal{
    name:string;
    constructor(name:string){
        this.name = name;
    }
    eat(str: string): void {
        console.log(this.name + '吃' + str);
        
    }
}
let dog = new Dog('小黑');
dog.eat('骨头');	// 小黑吃骨头
class Cat implements Animal{
    name: string;
    constructor(name:string){
        this.name = name;
    }
    eat(str: string): void {
        console.log(this.name + '吃' + str);
        
    }
}
let cat = new Cat('小花猫');
cat.eat('老鼠');	// 小花猫吃老鼠

# 6、接口扩展

接口可以继承接口

interface Animal{
    eat():void;
}
interface Person extends Animal{
    work():void;
}
class Programer{
    name:string;
    constructor(name:string){
        this.name = name;
    }
    coding(code:string):void{
        console.log(this.name + code);
        
    }
}
class Web extends Programer implements Person{
    // name:string;
    constructor(name:string){
        super(name);
        // this.name = name;
    }
    work(): void {
        console.log(this.name + '在工作');
        
    }
    eat(): void {
        console.log(this.name + '喜欢吃饭');
        
    }
}
let w = new Web('小李');
w.work();	// 小李在工作
w.eat();	// 小李喜欢吃饭
w.coding('敲代码');	// 小李敲代码

# 六、泛型

软件工程中。我们不仅要创建一致的定义良好的 API,同时也要考虑可重用性,组件不仅能够支持当前的数据类型,同时也能支持未来的数据类型,这在创建大型系统时为你提供了十分灵活的功能。

# 1、常规

通俗理解 泛型就是解决接口、类、方法的复用性,以及对不特定数据类型的支持

泛型一般用 T

// 同时返回 string 类型和 number 类型
// 泛型 可以支持不特定的数据类型 传入的参数和返回的参数一致
function getData<A>(value:A):A{
    return value;
}
console.log(getData<string>('123'));	// 参数必须是 string
console.log(getData<number>(123));	// 参数必须是 number
console.log(getData<boolean>(true));

# 2、泛型类

比如有个最小堆算法,需要同时支持返回数字和字符串两种类型。通过类的泛型来实现。

class MinClass<T>{
    public list: T[] = [];
    add(value: T): void {
        this.list.push(value);
    }
    min(): T {
        let minNum = this.list[0];
        for (let i = 0; i < this.list.length; i++) {
            if (minNum > this.list[i]) {
                minNum = this.list[i];
            }
        }
        return minNum;
    }
}
let m1 = new MinClass<number>();    // 实例化类 并且指定了类 T 代表的类型是 number
m1.add(1);
m1.add(3);
m1.add(2);
console.log(m1.min());
let m2 = new MinClass<string>();
m2.add('a');
m2.add('v');
m2.add('c');
console.log(m2.min());  // 判断 ascall 码值

# 3、泛型接口

// 第一种
interface ConfigFn{
    <T>(value:T):T;
}
let setData:ConfigFn = function<T>(value:T){
    return value;
}
console.log(setData<string>('张三'));
console.log(setData<number>(123));
// 第二种
interface ConfigFn<T>{
    (value:T):T;
}
function getData<T>(value:T):T{
    return value;
}
let myGetData:ConfigFn<string> = getData;
console.log(myGetData('20'));
//// 使用泛型
class MySqldb<T>{
    add(info:T):boolean{
        console.log(info);
        return true;
    }
    update(info:T,id:number):boolean{
        console.log(info);
        console.log(id);
        
        return true;
    }
}
//// 给 user 表增加数据
//// 定义一个 user 类
class User{
    userName:string | undefined;
    password: string | undefined;
}
let user = new User();
user.userName = '刘昕';
user.password = 'liuxin';
let db = new MySqldb<User>();
db.add(user);
// 定义一个 articleCate 类 和数据库表进行映射
class ArticleCate{
    title:string | undefined;
    desc:string | undefined;
    status:number | undefined;
    constructor(params:{
        title:string | undefined,
        desc:string | undefined,
        status?:number | undefined
    }){
        this.title = params.title;
        this.status = params.status;
        this.desc = params.desc;
    };
}
// 增加
let a = new ArticleCate({
    title:'分类',
    desc:'1111'
});
let articleDb = new MySqldb<ArticleCate>();
articleDb.add(a);
// 更新
let updateA = new ArticleCate({
    title:'分类',
    desc:'1111'
});
articleDb.update(updateA,2);

# 4、数据库接口示例

/*
    功能 定义一个操作数据库的库 支持 mysql mssql mongoDB
    要求 mtsql mssql mongoDb 功能一样 都有 add update delete get 方法
    注意 约束统一的规范 以及代码重用
    解决方案 需要约束规范所以要定义接口 需要代码重用所以需要使用泛型
    接口 在面向对象编程中 接口是一种规范的定义 它定义了行为和动作的规范
    泛型 通俗理解 泛型就是解决 类 接口 方法的复用性
*/
interface DBI<T>{
    add(info:T):boolean;
    update(info:T,id:number):boolean;
    delete(id:number):boolean;
    get(id:number):any[];
}
// 定义一个操作 mysql 数据库的类
// 要实现泛型接口 这个类也应该是泛型类
class MySqlDB<T> implements DBI<T>{
    constructor(){
        console.log('mysql数据库建立连接');
    }
    add(info: T): boolean {
        console.log(info);
        return true;
    }
    update(info: T, id: number): boolean {
        console.log(info);
        console.log(id);
        return true;
    }
    delete(id: number): boolean {
        console.log(id);
        return true;
    }
    get(id: number): any[] {
        console.log(id);
        return [{userName:'liuxin',password:'123456'}];
    }
    
}
// 定义一个操作 myssql 数据库的类
class MssqlDB<T> implements DBI<T>{
    constructor(){
        console.log('mssql数据库建立连接');
        
    }
    add(info: T): boolean {
        console.log(info);
        return true;
    }
    update(info: T, id: number): boolean {
        console.log(info);
        console.log(id);
        return true;
    }
    delete(id: number): boolean {
        console.log(id);
        return true;
    }
    get(id: number): any[] {
        console.log(id);
        return [{userName:'liuxin',password:'123456'}];
    }
    
}
// 定义一个操作 mongoDB 数据库的类
class MongoDB<T> implements DBI<T>{
    constructor(){
        console.log('mongo数据库建立连接');
    }
    add(info: T): boolean {
        console.log(info);
        return true;
    }
    update(info: T, id: number): boolean {
        console.log(info);
        console.log(id);
        return true;
    }
    delete(id: number): boolean {
        console.log(id);
        return true;
    }
    get(id: number): any[] {
        console.log(id);
        return [{userName:'liuxin',password:'123456'}];
    }
}
// 操作用户表 定义一个 User 类和数据表做映射
class User{
    userName:string | undefined;
    password:string | undefined;
}
let u = new User();
u.userName = '张三';
u.password = 'zhangsan';
let mySql = new MySqlDB<User>();
mySql.add(u);
let mssql = new MssqlDB<User>();
// 添加数据
mssql.add(u);
// 更新数据
mssql.update(u,1);
// 获取数据
console.log(mssql.get(1));

# 5、模块化

就是 es6 模块化的 ts 版

/*
    模块的概念
    内部模块 称作为 命名空间
    外部模块 称作为 模块
    模块在其自身的作用域里执行 而不是全局作用域里
    export 导出需要被其他模块访问到的变量或者函数等
    import 导入需要引用的其他模块的变量或者函数等
*/

# 6、命名空间

/* 
    命名空间
        在代码量较大的情况下,为了避免各种变量命名相冲突,可以将类似功能的函数、类、接口等放置到命名空间内
        同 java 的包、.net 的命名空间一样,typescript 的命名空间可以将代码包裹起来,只对外暴露需要在外部访问的对象。
        命名空间内的对象通过 export 导出
    命名空间与模块的区别
        命名空间 内部模块 主要用于组织代码,避免命名冲突
        模    块 ts 的外部模块的简称,侧重代码的复用,一个模块里可能会有多个命名空间
*/
namespace A{
    interface Animal{
        name:string;
        eat():void;
    }
    
    export class Dog implements Animal{
        name:string;
        constructor(name:string){
            this.name = name;
        }
        eat(): void {
            console.log(this.name + '吃狗粮');
            
        }
    }
    
    export class Cat implements Animal{
        name: string;
        constructor(name:string){
            this.name = name;
        }
        eat(): void {
            console.log(this.name + '吃猫粮');
            
        }
    }
}
    
namespace B{
    interface Animal{
        name:string;
        eat():void;
    }
    
    export class Dog implements Animal{
        name:string;
        constructor(name:string){
            this.name = name;
        }
        eat(): void {
            console.log(this.name + '吃狗粮');
            
        }
    }
    
    export class Cat implements Animal{
        name: string;
        constructor(name:string){
            this.name = name;
        }
        eat(): void {
            console.log(this.name + '吃猫粮');
            
        }
    }
}
    
let d = new A.Dog('狼狗');
d.eat();
let c = new B.Dog('小花');
c.eat();

# 七、装饰器

  • 是一种特殊类型的声明 它能够被附加到类声明,方法,属性或参数上,可以修改类的行为
  • 通俗的讲装饰器就是一个方法,可以注入到类、方法、属性参数上来扩展类、属性、方法、参数的功能
  • 常见装饰器有 类装饰器 属性装饰器 方法装饰器 参数装饰器
  • 装饰器的写法 普通装饰器 (无法传参) 装饰器工厂 (可传参)
  • 装饰器是过去几年中 js 最大的成就之一,已经是 es7 的标准特性之一

装饰器执行顺序

属性 > 方法 > 方法参数 (多个同类型装饰器顺序从后往前)> 类装饰器

// 装饰器
function logClass(params:any):void{
    console.log(params);
    //params 就是当前类
    
    params.prototype.apiUrl = '动态拓展的属性';
    params.prototype.run = function(){
        console.log('我是一个run方法');
        
    }
}
@logClass
class HttpClient{
    constructor(){
    }
    getData(){
    }
}
let http = new HttpClient();
console.log(http.apiUrl);	// 动态拓展的方法
http.run();		// 我是一个 run 方法
// 装饰器工厂
function logClass(params:string){
    return function(target:any){
        console.log(params);	//http://www.baidu.com/
        console.log(target);
        target.prototype.apiUrl = params;
    }
}
@logClass('http://www.baidu.com/')
class HttpClient{
    constructor(){
    }
    getData(){
    }
}
let http = new HttpClient();
console.log(http.apiUrl);	//http://www.baidu.com/

# 1、类装饰器

function logClass(target:any){
    console.log(target);
    return class extends target{
        apiUrl:any = '我是修改后的apiUrl';
        getData(){
            this.apiUrl = this.apiUrl + '----';
            console.log(this.apiUrl);
        }
    }
}
@logClass
class HttpClient{
    public apiUrl:string | undefined;
    constructor(){
        this.apiUrl = '我是构造函数里的apiUrl';
    }
    getData(){
        console.log(this.apiUrl);
        
    }
}
let http = new HttpClient();
http.getData();	// 我是修改后的 apiUrl----

# 2、属性装饰器

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

  • 对于静态成员来说是类的构造函数,对于实例成员来说是类的原型对象
  • 成员的名字
// 属性装饰器
function logClass(params:string){
    return function(target:any){
        // console.log(target);
        // console.log(params);
        // target.prototype.apiUrl = params;
    }
}
function logProperty(params:any){
    return function(target:any,attr:any){
        console.log(target);	// {}
        console.log(attr);		// url
        target[attr] = params;
    }
}
@logClass('http://www.baidu.com/')
class HttpClient{
    @logProperty('http://www.liuxin.com/')
    public url:any | undefined;
    constructor(){
        
    }
    getData(){
        console.log(this.url);
        
        
    }
}
let http:any = new HttpClient();
http.getData();//http://www.liuxin.com/

# 3、方法装饰器

它会被应用到方法的属性描述符上,可以用来监视,修改或者替换方法定义

方法装饰会在运行时传入下列 3 个参数

  • 对于静态成员来说是类的构造函数,对于实力成员是类的原型对象
  • 成员名字
  • 成员的属性描述符
// 方法装饰器一
function get(params:any){
    return function(target:any,methodName:any,desc:any){
        console.log(target);
        console.log(methodName);
        console.log(desc);
        target.apiUrl = 'xxx';
        target.run = function(){
            console.log('我是一个方法');
            
        }
    }
}
class HttpClient{
    public url:any | undefined;
    constructor(){
    }
    @get('http://www.baidu.com/')
    getData(){
    }
}
let http = new HttpClient();
console.log(http.apiUrl);
http.run();
// 方法装饰器二
function get(params:any){
    return function(target:any,methodName:any,desc:any){
        console.log(target);
        console.log(methodName);
        console.log(desc.value);
        // 修改装饰器的方法 把装饰器方法里面传入的所用参数改为 string 类型
        
        // 保存当前的方法
        let oMethod = desc.value;
        // 修改
        desc.value = function(...args:any[]){
            args = args.map((value) =>{
                return String(value);
            });
            // console.log(args);
            oMethod.apply(this,args);
        }
    }
}
class HttpClient{
    public url:any | undefined;
    constructor(){
    }
    @get('http://www.baidu.com/')
    getData(...args:any[]){
        console.log(args);
        console.log('我是getData里的方法');
        
    }
}
let http = new HttpClient();
http.getData(123,'xxx');	//['123','xxx'];

# 4、方法参数装饰器

参数装饰器表达式会在运行时当作函数被调用,可以使用参数装饰器为类的原型增加一些元素数据,传入下列 3 个参数:

  • 对于静态成员来说是类的构造函数,对于实例成员是类的原型对象
  • 方法的名字
  • 参数在函数参数列表中的索引
function logParams(params:any){
    return function(target:any,paramsName:any,paramsIndex:any){
        console.log(target);
        console.log(paramsName);
        console.log(paramsIndex);
        target.apiUrl = params;
    }
}
class HttpClient{
    public url:any | undefined;
    constructor(){
    }
    getData(@logParams('xxx') uuid:any){
        console.log(uuid);
        
    }
}
let http:any = new HttpClient();
http.getData(123456);
console.log(http.apiUrl);