TypeScript语法

93 阅读9分钟

一、基本数据类型

1.布尔类型(boolean)


var flag:boolean = true;
flag = false

3.数字类型(number)

var num:number = 123

3.字符串类型(string)

var str:string = 'this is ts'

str = 'haha'

4.数组类型(array)

ts中定义数组有两种方式

① 第一种定义数组的方式

var arr:number[] = [11,22,33]
 console.log(arr)
var arr:Array<number> = [11,22,33]

② 第二种定义数组的方式

var arr:Array<number>=[11,22,33];
console.log(arr)

5.元组类型(tuple) 属于数组的一种

var arr:Array<number>=[11,22,33];
console.log(arr)
let arr:[number,string]=[123,'this is ts'];
console.log(arr);

6.枚举类型(enum)

随着计算机的不断普及,程序不仅只用于数值计算,还更广泛地用于处理非数值的数据。

例如:性别、月份、星期几、颜色、单位名、学历、职业等,都不是数值数据。

在其它程序设计语言中,一般用一个数值来代表某一状态,这种处理方法不直观,易读性差。

如果能在程序中用自然语言中有相应含义的单词来代表某一状态,则程序就很容易阅读和理解。

也就是说,事先考虑到某一变量可能取的值,尽量用自然语言中含义清楚的单词来表示它的每一个值,

这种方法称为枚举方法,用这种方法定义的类型称枚举类型。

enum 枚举名{ 
      标识符[=整型常数], 
          标识符[=整型常数], 
          ... 
      标识符[=整型常数], 
  } ;    
enum Flag {success=1, error=2}
let s:Flag = Flag.success;
console.log(s)
​
​
enum Color {blue, red, 'orange'};
var c:Color=Color.blue;
console.log(c)
​
enum Err {'undefined'=1, 'null'=-2, 'success'=1}
var e:Err = Err.success;
console.log(e)

7.任意类型(any)

     var num:any=123;
     num='str';
     num=true;
     console.log(num)
 //任意类型的用处
var oBox:any=document.getElementById('box');
oBox.style.color='red';

8.null 和 undefined

其他(never类型)数据类型的子类型

 var num2:number;
 console.log(num2)
var num3:undefined;
console.log(num3)

一个元素可能是 number类型 可能是null 可能是undefined,用|表示可能的数据类型

var num:number | null | undefined;
​
num=1234;
​
console.log(num)

9.void类型

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

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

10.never类型

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

箭头函数返回的值可以定义为never类型

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

二、TypeScript的函数

1、函数的定义

es5定义函数的方法

//函数声明法
function run(){
​
    return 'run';
}
//匿名函数
var run2=function(){
​
    return 'run2';
}

ts中定义函数的方法

//函数声明法
function run():string{
    return 'run';
}
// 匿名函数
var fun2=function():number{
    return 123;
}
alert(fun2()); /*调用方法*/

ts中定义方法传参

function getInfo(name:string,age:number):string{
    return `${name} --- ${age}`;
}
alert(getInfo('zhangsan',20));
    

没有返回值的方法

function run():void{
    console.log('run')
}
run();

2、方法可选参数

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

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

注意:可选参数必须配置到参数的最后面

3、默认参数 可选参数

 // es5里面没法设置默认参数,es6和ts中都可以设置默认参数function getInfo(name:string,age:number=20):string{
    if(age){
        return `${name} --- ${age}`;
    }else{
        return `${name} ---年龄保密`;
    }
}
​
// alert( getInfo('张三'));
alert( getInfo('张三',30));

4、剩余参数

function sum(...result:number[]):number{
    var sum=0;
    for(var i=0;i<result.length;i++){
        sum+=result[i];  
    }
    return sum;
}
alert(sum(1,2,3,4,5,6)) ;

5、ts函数重载

// java中方法的重载:重载指的是两个或者两个以上同名函数,但它们的参数不一样,这时会出现函数重载的情况。

// typescript中的重载:通过为同一个函数提供多个函数类型定义来试下多种功能的目的。

//ts为了兼容es5 以及 es6 重载的写法和java中有区别。

//es5中出现同名方法,下面的会替换上面的方法

 function css(config){
 }
function css(config,value){
}
  function getInfo4(name:string):string;
  function getInfo4(age:number):string;
  function getInfo4(str:any):any{
    if (typeof str === 'string') {
      return "我叫:" + str; 
    } else {
      return "我的年龄是" + str
    }
  }
​
   console.log(getInfo4("张三"))
   console.log(getInfo4(23))

6、箭头函数 es6

this指向的问题 箭头函数里面的this指向上下文

 setTimeout(()=>{
​
            alert('run')
        },1000)

三、类

1、es5里面的类

1️⃣ 最简单的类

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

2️⃣构造函数和原型链里面增加方法

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

3️⃣ 类里面的静态方法

function Person3() {
  this.name = "张三",
  this.age = 20;
  this.shit = function () {
    console.log(this.name + "在拉屎")
  }
  // 静态方法
  Person3.getInfo = function () {
    console.log("我是静态方法")
  }
​
  // 原型链上面的属性会被多个实例共享  构造函数不会
  Person3.prototype.sex = "男";
  Person3.prototype.work = function () {
    console.log(this.name + "在工作")
  }
}
var p3 = new Person3();
// p3.work()
// Person3.getInfo()

4️⃣es5里面的继承 对象冒充实现继承

function Person(){
    this.name='张三';  /*属性*/
    this.age=20;
    this.run=function(){  /*实例方法*/
        alert(this.name+'在运动');
    }
​
}      
Person.prototype.sex="男";
Person.prototype.work=function(){
    alert(this.name+'在工作');
​
}
​
//Web类 继承Person类   原型链+对象冒充的组合继承模式function Web(){
    Person.call(this);    /*对象冒充实现继承*/
}
​
var w=new Web();
// w.run();  //对象冒充可以继承构造函数里面的属性和方法
​
w.work();  //对象冒充可以继承构造函数里面的属性和方法   但是没法继承原型链上面的属性和方法

5️⃣es5里面的继承 原型链实现继承

   function Person(){
            this.name='张三';  /*属性*/
            this.age=20;
            this.run=function(){  /*实例方法*/
                alert(this.name+'在运动');
            }
​
        }      
        Person.prototype.sex="男";
        Person.prototype.work=function(){
             alert(this.name+'在工作');
​
        }
       
        //Web类 继承Person类   原型链+对象冒充的组合继承模式
​
        function Web(){
         
        }
​
       Web.prototype=new Person();   //原型链实现继承
       var w=new Web();
        //原型链实现继承:可以继承构造函数里面的属性和方法 也可以继承原型链上面的属性和方法
        //w.run();
​
        w.work();

6️⃣原型链实现继承的 问题?传参的时候找不到属性

function Person(name,age){
    this.name=name;  /*属性*/
    this.age=age;
    this.run=function(){  /*实例方法*/
        alert(this.name+'在运动');
    }
}      
Person.prototype.sex="男";
Person.prototype.work=function(){
    alert(this.name+'在工作');
}
​
var p=new Person('李四',20);
p.run();
​
function Person(name,age){
    this.name=name;  /*属性*/
    this.age=age;
    this.run=function(){  /*实例方法*/
        alert(this.name+'在运动');
    }
}      
Person.prototype.sex="男";
Person.prototype.work=function(){
    alert(this.name+'在工作');
​
}
​
​
function Web(name,age){
}
​
Web.prototype=new Person();
var w=new Web('赵四',20);   //实例化子类的时候没法给父类传参
w.run();
// var w1=new Web('王五',22);

7️⃣原型链+对象冒充的组合继承模式

  function Person(name,age){
            this.name=name;  /*属性*/
            this.age=age;
            this.run=function(){  /*实例方法*/
                alert(this.name+'在运动');
            }
    }      
    Person.prototype.sex="男";
    Person.prototype.work=function(){
            alert(this.name+'在工作');
    }
       
      
    function Web(name,age){
        Person.call(this,name,age);   //对象冒充继承   实例化子类可以给父类传参
    }
​
    Web.prototype=new Person();
    var w=new Web('赵四',20);   //实例化子类的时候没法给父类传参
    // w.run();
    w.work();
​
    // var w1=new Web('王五',22);

8️⃣原型链+对象冒充继承的另一种方式

function Person(name,age){
            this.name=name;  /*属性*/
            this.age=age;
            this.run=function(){  /*实例方法*/
                alert(this.name+'在运动');
            }
    }      
    Person.prototype.sex="男";
    Person.prototype.work=function(){
            alert(this.name+'在工作');
​
    }
       
    function Web(name,age){
        Person.call(this,name,age);   //对象冒充继承  可以继承构造函数里面的属性和方法、实例化子类可以给父类传参
    }
​
    Web.prototype=Person.prototype;
    var w=new Web('赵四',20);   //实例化子类的时候没法给父类传参
     w.run();
    // w.work();
​
    // var w1=new Web('王五',22);

2.类的定义

es5:

function Person(name){
    this.name=name;
​
    this.run=function(){
​
        console.log(this.name)
    }
}
​
var p=new Person('张三');
​
p.run()

ts中定义类:

class Person{
​
    name:string;   //属性  前面省略了public关键词
    constructor(n:string){  //构造函数   实例化类的时候触发的方法
        this.name=n;
    }
    run():void{
        alert(this.name);
    }
}
var p=new Person('张三');
​
p.run()

3.继承

class Person{
    name:string;
    constructor(name:string){
        this.name=name;
    }
    run():string{
        return `${this.name}在运动`
    }
}
// var p=new Person('王五');
// alert(p.run())class Web extends Person{
    constructor(name:string){
        super(name);  /*初始化父类的构造函数*/
    }
}
​
var w=new Web('李四');
alert(w.run());

ts中继承的探讨 父类的方法和子类的方法一致, 子类覆盖父类的方法

4. 类里面的修饰符

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

public :公有 在当前类里面、 子类 、类外面都可以访问

protected:保护类型 在当前类里面、子类里面可以访问 ,在类外部没法访问

private :私有 在当前类里面可以访问,子类、类外部都没法访问

属性如果不加修饰符 默认就是 公有 (public)

class Person7{
    public name:string;  /*公有属性*/
    constructor(name:string){
        this.name=name;
    }
​
    run():string{
        return `${this.name}在运动`
    }
}
var p7=new Person7('王五');
console.log(p7.run())
​
class Web7 extends Person7{
    constructor(name:string){
        super(name);  /*初始化父类的构造函数*/
    }                  
    work(){
        console.log(`${this.name}在工作`)
    }
}
​
var w7=new Web7('李四11');
w7.work();
console.log(w7.name)
console.log(w7.run());

5. 静态属性 静态方法

class Person {
  public name:string;
  public age:number=20;
  // 静态属性
​
  static sex = '男'
  constructor(name:string) {
    this.name = name;
  }
  run () { // 实例方法
    console.log(`${this.name}在运动`)
  }
​
  work () {
    console.log(`${this.name}在工作`)
  }
  static print () {
    console.log('print方法' + Per.sex)
  }
}
​
var person = new Per("张三")
// person.run()
// Person.print()
// console.log(Person.sex)

6. 多态 抽象类

1️⃣多态:父类定义一个方法不去实现,让继承它的子类去实现 每一个子类有不同的表现

class Animal {
​
    name:string;
    constructor(name:string) {
        this.name=name;
    }
    eat(){   //具体吃什么  不知道   ,  具体吃什么?继承它的子类去实现 ,每一个子类的表现不一样
        console.log('吃的方法')
    }
}
​
class Dog extends Animal{
    constructor(name:string){
        super(name)
    }
    eat(){
​
        return this.name+'吃粮食'
    }
}
​
​
class Cat extends Animal{
​
    constructor(name:string){
        super(name)
    }
​
    eat(){
​
        return this.name+'吃老鼠'
    }
}

2️⃣抽象类:

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

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

// abstract抽象方法只能放在抽象类里面

// 抽象类和抽象方法用来定义标准 。 标准:Animal 这个类要求它的子类必须包含eat方法

//标准:

abstract class Animal{
    
    public name:string;
    constructor(name:string){
​
        this.name=name;
​
    }
    abstract eat():any;  //抽象方法不包含具体实现并且必须在派生类中实现。
    
    run(){
​
        console.log('其他方法可以不实现')
    }
}
// var a=new Animal() /*错误的写法*/
class Dog extends Animal{
    //抽象类的子类必须实现抽象类里面的抽象方法
    constructor(name:any){
        super(name)
    }
    eat(){
        console.log(this.name+'吃粮食')
    }
}
​
var d=new Dog('小花花');
d.eat();

四、接口

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

1 属性类接口:

// 1-1.对json的约束

// ts中自定义方法传入参数,对json进行约束

function printLabel(labelInfo:{label:string}):void {
  console.log("printLabel")
}
​
// printLabel({label: '张三'})

// 1-2.完整接口

//对批量方法传入参数进行约束。

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

interface FullName {
  firstName:string;
  secondName:string;
}
​
function printName(name:FullName) {
  console.log(name.firstName + '--' + name.secondName)
}
​
// printName({
//   firstName: '张',
//   secondName: '伟'
// })

// 1-3.可选属性

 interface FullName2{
​
      firstName:string;
      secondName?:string;
  }
​
  function getName(name:FullName2){
​
      console.log(name)
  }
 //参数的顺序可以不一样
  // getName({        
  //     firstName:'firstName'
  // })

1-4.原生js封装的ajax

 interface Config {
    type: string;
    url: string;
    data?: string;
    dataType:string;
  }
​
  function ajax(config:Config) {
    var xhr = new XMLHttpRequest ();
    xhr.open(config.type, config.url, true);
    xhr.send(config.data)
    xhr.onreadystatechange = function () {
      if (xhr.readyState == 4 && xhr.status == 200) {
        console.log("success")
​
        if (config.dataType == 'json') {
          console.log(JSON.parse(xhr.responseText))
        } else {
          console.log(xhr.responseText)
        }
      }
    }
  }
​
   ajax({
     type: 'get',
     data: 'name=zhangsan',
     url: 'http://a.itying.com/api/productlist',
     dataType: 'json'
   })

2 函数类型接口

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

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

3 可索引接口

可索引接口:数组、对象的约束 (不常用)

ts定义数组的方式

var arr:number[] = [2342, 235325]
var arr1:Array<string> = ['111', '22222']
​
var arr2:Array<number> = [12,324]

1️⃣可索引接口 对数组的约束

interface UserArr {
  [index: number]:string // 索引是数组,值是字符串
}

2️⃣可索引接口 对对象的约束

interface UserObj {
  [index:string]:string
}
var arr4:UserObj = {name: "朱一旦"}
console.log(arr4['name'])

4 类类型接口

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

interface Animal {
  name:string;
  eat(str:string):void // 实现类必须有这个方法
}
​
class Dog implements Animal {
  name:string;
  constructor(name:string) {
    this.name = name
  }
​
  eat () {
    console.log(this.name + '吃粮食')
  }
}
​
var dog = new Dog("小黑")
// dog.eat()

5.​接口扩展

1️⃣接口可以继承接口

interface Animal{
  eat():void;
}
​
interface Person extends Animal {
  work():void
}
​
class Web implements Person {
  public name:string;
  constructor(name:string) {
    this.name = name
  }
  eat () {
    console.log(this.name + '喜欢吃馒头')
  }
  work () {
    console.log(this.name + '写代码')
  }
}
​
var web = new Web('小李')
// web.work()
// web.eat()

2️⃣即继承类又实现接口

interface Animal2{
  eat():void;
}
​
interface Person2 extends Animal2 {
  work():void
}
​
class Programmer2{
  public name:string;
  constructor(name:string) {
    this.name = name
  }
  coding(code:string) {
    console.log(this.name + code)
  }
}
​
class Web2 extends Programmer2 implements Person2 {
  constructor(name:string){
    super(name)
  }
  eat(){
      console.log(this.name+'喜欢吃馒头')
  }
  work(){
      console.log(this.name+'写代码');
  }
}
​
var web2=new Web2('小王');
web2.coding('写ts代码');

五、泛型

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

在像C#和Java这样的语言中,可以使用泛型来创建可重用的组件,一个组件可以支持多种类型的数据。 这样用户就可以以自己的数据类型来使用组件。

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

1 泛型的定义

//1-1. 定义一个函数返回string类型或者number类型,any可以解决这个问题,但舍弃了类型检查,即传入什么 返回什么。比如:传入number 类型必须返回number类型 传入 string类型必须返回string类型

function getData (value:any): any {
 return '哈哈哈'
​
}
​
​
// getData(123)
// getData('str')

泛型:可以支持不特定的数据类型 要求:传入的参数和返回的参数一致

T表示泛型,具体什么类型是调用这个方法的时候决定的

function getData2<T> (value: T):T {
  return value
}
​
 console.log(getData2<string>('马勒戈壁'))
 console.log(getData2<number>(3412))

2 泛型函数

同上

3 泛型类

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

class MinClass<T> {
  public list:T[] = []
  add(value:T):void {
    this.list.push(value)
  }
​
  min():T {
    var minNum = this.list[0]
    for (var i = 0; i < this.list.length; i++) {
      if (minNum > this.list[i]) {
        minNum = this.list[i]
      }
    }
    return minNum;
  }
}
​
var m1 = new MinClass<number>()
m1.add(11)
m1.add(3)
m1.add(2)
// console.log(m1.min())var m2 = new MinClass<string>()
m2.add('c')
m2.add('a');
m2.add('v');
// console.log(m2.min())

4 泛型接口

定义接口的时候不确定数据类型

1️⃣函数类型接口

interface ConfigFn {
  <T>(value:T):T;
}
​
var getData3:ConfigFn = function<T>(value:T):T {
  return value
}
​
console.log(getData3<string>("张三"))
console.log(getData3<number>(1445))

2️⃣ 泛型案例:操作数据库

2-1.把类作为参数来约束数据传入的类型:将User类作为参数传给MysqlDb

class User {
  username:string | undefined;
  password:string | undefined;
}
​
class MysqlDb {
  add(user:User):boolean{
    console.log(user)
    return true
  }
}
​
var u = new User()
u.username = "张三"
u.password = '123456'var Db = new MysqlDb();
Db.add(u)
​
// 问题:代码重复,这个时候再来一个ArticleCate,又要重新定义一个接收ArticleCate的MysqlDb类

2-2.定义操作数据库的泛型类,不用声明传入的是哪个类

class MysqlDb2<T> {
  add(info:T):boolean{
    console.log(info)
    return true
  }
  updated(info:T, id:number):boolean{
    console.log(info)
    console.log(id)
    return true
  }
}
​
class User2{
    username:string | undefined;
    password:string | undefined;
}
​
var u2 = new User2 ();
u2.username = '你爸'
u2.password = '8888888'
var Db2 = new MysqlDb2<User2>()
Db2.add(u2)
Db2.updated(u2, 2333)

2-3.定义一个操作数据库的库 支持 Mysql Mssql MongoDb

要求1:Mysql 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 MysqlDb3<T> implements DBI<T> {
  constructor() {
    console.log('数据建立连接')
  }
​
  add(info: T) {
    console.log(info)
    return true
  }
​
  update(info:T, id:number): boolean {
    throw new Error("Method not implement")
  }
  delete(id:number): boolean{
    throw new Error("Method not implement")
  }
​
  get(id: number):any [] {
    var list = [
      {
        title: 'xxx',
        desc: 'xxxxxxxxx'
      },
      {
        title: 'xxx',
        desc: 'xxxxxxxxx'
      }
    ]
    return list
  }
}
​
class User3 {
  username:string | undefined;
  password:string | undefined;
}
​
var u3=new User3();
u3.username='张三111';
u3.password='123456';
​
var oMysql3 = new MysqlDb3<User>()
oMysql3.add(u3)
console.log(oMysql3.get(1))

// 这时来了另一个数据库MsSqlDb,同样适用

六、模块

1 模块的的概念

模块的的概念(官方):

关于术语的一点说明: 请务必注意一点,TypeScript 1.5里术语名已经发生了变化。 “内部模块”现在称做“命名空间”。“外部模块”现在则简称为“模块” 模块在其自身的作用域里执行,而不是在全局作用域里;这意味着定义在一个模块里的变量,函数,类等等在模块外部是不可见的,除非你明确地使用export形式之一导出它们。相反,如果想使用其它模块导出的变量,函数,类,接口等的时候,你必须要导入它们,可以使用 import形式之一。

模块的概念(自己理解):我们可以把一些公共的功能单独抽离成一个文件作为一个模块。模块里面的变量 函数 类等默认是私有的,如果我们要在外部访问模块里面的数据(变量、函数、类),我们需要通过export暴露模块里面的数据(变量、函数、类...)。暴露后我们通过 import 引入模块就可以使用模块里面暴露的数据(变量、函数、类...)。

var dbUrl = 'xxxxxx'function getData():any[]{
  console.log("获取数据库的数据111")
  return [
    {
      title: '迪丽热🐎'
    },
    {
      title: '古力娜扎'
    }
  ]
}
​
function save() {
  console.log("保存数据成功")
}
​
export {dbUrl, getData, save}
import {dbUrl, getData as get} from './modules/db'
​
console.log(dbUrl)
get();

2 模块导出的几种方法(不详细写)

1、export 导出声明

2、export 导出语句

3、export default

4、import导入模块

3 模块化封装上一讲的DB库

db.ts:封装了对数据库的操作,使用了泛型,可适用于不同的数据库

interface DBI<T>{
  add(info:T):boolean;
  update(info:T,id:number):boolean;
  delete(id:number):boolean;
  get(id:number):any[];
}
​
//定义一个操作mysql数据库的类       注意:要实现泛型接口 这个类也应该是一个泛型类export class MysqlDb<T> implements DBI<T>{
​
  constructor(){
​
      console.log('数据库建立连接');
  }
  add(info: T): boolean {
​
      console.log(info);
​
      return true;
     
  }    
  
  update(info: T, id: number): boolean {
      throw new Error("Method not implemented.");
  }
  delete(id: number): boolean {
      throw new Error("Method not implemented.");
  }
  get(id: number): any[] {
      var list=[
​
          {
              title:'xxxx',
              desc:'xxxxxxxxxx'
          },
          {
              title:'xxxx',
              desc:'xxxxxxxxxx'
          }
      ]
​
      return list;
  }
  
}
​

article.ts: 实体类,在MsSqlDb数据库进行读写

import {MsSqlDb} from '../modules/db';
​
//定义数据库的映射
class UserClass{
    username:string | undefined;
    password:string | undefined;
}
​
​
var UserModel=new MsSqlDb<UserClass>();
export {
    UserClass,UserModel
}
​

user.ts:实体类,在MsSqlDb数据库进行读写

import {MsSqlDb} from '../modules/db';
​
//定义数据库的映射
class UserClass{
    username:string | undefined;
    password:string | undefined;
}
​
​
var UserModel=new MsSqlDb<UserClass>();
export {
    UserClass,UserModel
}
​

index.ts: 具体操作

import {UserClass, UserModel} from './model/user'
import {ArticleClass, ArticleModel} from './model/article'
​
​
// 增加数据
var u = new UserClass()
u.username = '张三'
u.password = '121212144';
UserModel.add(u)
​
​
// 获取user表数据
// var res = UserModel.get(123)
// console.log(res)
​
​
// 获取文章表的数据
// var aRes = ArticleModel.get(1464)
// console.log(aRes)
​
​

七、命名空间

命名空间:

在代码量较大的情况下,为了避免各种变量命名相冲突,可将相似功能的函数、类、接口等放置到命名空间内

同Java的包、.Net的命名空间一样,TypeScript的命名空间可以将代码包裹起来,只对外暴露需要在外部访问的对象。命名空间内的对象通过export关键字对外暴露。

命名空间和模块的区别

命名空间:内部模块,主要用于组织代码,避免命名冲突。

模 块:ts的外部模块的简称,侧重代码的复用,一个模块里可能会有多个命名空间。

namespace A {
  interface Animal {
    name:string;
    eat(): void;
  }
​
  export class Dog implements Animal {
    name:string;
    constructor(theName: string) {
      this.name = theName
    }
​
    eat () {
      console.log(`${this.name} 在吃狗粮。`)
    }
  }
​
  export class Cat implements Animal {
    name:string;
    constructor(theName: string) {
      this.name = theName
    }
​
    eat () {
      console.log(`${this.name} 在吃猫粮。`)
    }
  }
}
​
​
namespace B {
  interface Animal {
    name:string;
    eat(): void;
  }
​
  export class Dog implements Animal {
    name:string;
    constructor(theName: string) {
      this.name = theName
    }
​
    eat () {
      console.log(`${this.name} 在吃狗粮2。`)
    }
  }
​
  export class Cat implements Animal {
    name:string;
    constructor(theName: string) {
      this.name = theName
    }
​
    eat () {
      console.log(`${this.name} 在吃猫粮2。`)
    }
  }
}
​
var c = new A.Cat("小花")
c.eat()
​

八、装饰器

装饰器:装饰器是一种特殊类型的声明,它能够被附加到类声明,方法,属性或参数上,可以修改类的行为。

通俗的讲装饰器就是一个方法,可以注入到类、方法、属性参数上来扩展类、属性、方法、参数的功能。

常见的装饰器有:类装饰器、属性装饰器、方法装饰器、参数装饰器

装饰器的写法:普通装饰器(无法传参) 、 装饰器工厂(可传参)

装饰器是过去几年中js最大的成就之一,已是Es7的标准特性之一

1、类装饰器

类装饰器在类声明之前被声明(紧靠着类声明)。 类装饰器应用于类构造函数,可以用来监视,修改或替换类定义。 传入一个参数

1️⃣类装饰器:普通装饰器(无法传参)

function logClass(params:any){
    console.log(params);
    // params 就是当前类
    params.prototype.apiUrl='动态扩展的属性';
    params.prototype.run=function(){
        console.log('我是一个run方法');
    }
​
}
​
@logClass
class HttpClient{
    constructor(){
    }
    getData(){
​
    }
}
var http:any=new HttpClient();
console.log(http.apiUrl);
http.run();

2️⃣类装饰器:装饰器工厂(可传参)

 function logClass(params:string){
     return function(target:any){
         console.log(target);
         console.log(params);
         target.prototype.apiUrl=params;
     }
 }
​
@logClass('http://www.itying.com/api')
class HttpClient{
    constructor(){
    }
​
    getData(){
​
    }
}
​
var http:any=new HttpClient();
console.log(http.apiUrl);

3️⃣类装饰器-重载构造函数

类装饰器表达式会在运行时当作函数被调用,类的构造函数作为其唯一的参数。

如果类装饰器返回一个值,它会使用提供的构造函数来替换类的声明。

function logClass(target:any){
    console.log(target);
    return class extends target{
        apiUrl:any='我是修改后的数据';
        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);
    }
}
​
var http=new HttpClient();
http.getData();

2、属性装饰器

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

1、对于静态成员来说是类的构造函数,对于实例成员是类的原型对象。

2、成员的名字。

function logProperty(params:any){
    return function(target:any,attr:any){
        console.log(target);
        console.log(attr);
        target[attr]=params;
    }
}
@logClass('属性被人改了')
class HttpClient{
    @logProperty('http://itying.com')
    public url:any |undefined;
    constructor(){
    }
    getData(){
        console.log(this.url);
    }
}
var http=new HttpClient();
http.getData();

3、方法装饰器

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

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

1、对于静态成员来说是类的构造函数,对于实例成员是类的原型对象。

2、成员的名字。

3、成员的属性描述符。

1️⃣ 方法装饰器一

function get(params:any){
    return function(target:any,methodName:any,desc:any){
        console.log(target);
        console.log(methodName);
        console.log(desc);
        target.apiUrl='xxxx';
        target.run=function(){
            console.log('run');
        }
    }
}
​
class HttpClient{  
    public url:any |undefined;
    constructor(){
    }
    @get('http://www.itying,com')
    getData(){
        console.log(this.url);
    }
}
​
var http:any=new HttpClient();
console.log(http.apiUrl);
http.run();

2️⃣方法装饰器二

function get(params:any){
    return function(target:any,methodName:any,desc:any){
        console.log(target);
        console.log(methodName);
        console.log(desc.value);       
​
        //修改装饰器的方法  把装饰器方法里面传入的所有参数改为string类型
​
        //1、保存当前的方法
​
        var oMethod=desc.value;
        desc.value=function(...args:any[]){                
            args=args.map((value)=>{
                return String(value);
            })
            oMethod.apply(this,args);
        }
​
    }
}
​
class HttpClient{  
    public url:any |undefined;
    constructor(){
    }
    @get('http://www.itying,com')
    getData(...args:any[]){
        console.log(args);
        console.log('我是getData里面的方法');
    }
}
​
var http=new HttpClient();
http.getData(123,'xxx');

4、方法参数装饰器

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

1、对于静态成员来说是类的构造函数,对于实例成员是类的原型对象。

2、方法的名字。

3、参数在函数参数列表中的索引。

function logParams(params:any){
    return function(target:any,methodName:any,paramsIndex:any){
        console.log(params);
        console.log(target);
        console.log(methodName);
        console.log(paramsIndex);
        target.apiUrl=params;
    }   
​
}
​
class HttpClient7{  
    public url:any |undefined;
    constructor(){
    }           
    getData(@logParams('xxxxx') uuid:any){               
        console.log(uuid);
    }
 }
​
​
  var http7:any = new HttpClient7();
  http7.getData(123456);
 console.log( http7.apiUrl);

5、装饰器执行顺序

  • 属性 > 方法 > 方法参数 > 类
  • 如果有多个同样的装饰器,它会先执行后面的
function logClass1(params:string){
    return function(target:any){
      console.log('类装饰器1')
    }
}
​
function logClass2(params:string){
    return function(target:any){
      console.log('类装饰器2')
    }
}
​
function logAttribute1(params?:string){
    return function(target:any,attrName:any){
      console.log('属性装饰器1')
    }
}
​
function logAttribute2(params?:string){
    return function(target:any,attrName:any){
      console.log('属性装饰器2')
    }
}
​
function logMethod1(params?:string){
    return function(target:any,attrName:any,desc:any){
      console.log('方法装饰器1')
    }
}
function logMethod2(params?:string){
    return function(target:any,attrName:any,desc:any){
      console.log('方法装饰器2')
    }
}
​
​
​
function logParams1(params?:string){
    return function(target:any,attrName:any,desc:any){
      console.log('方法参数装饰器1')
    }
}
​
function logParams2(params?:string){
    return function(target:any,attrName:any,desc:any){
      console.log('方法参数装饰器2')
    }
}
​
​
​
@logClass1('http://www.itying.com/api')
@logClass2('xxxx')
class HttpClient{
    @logAttribute1()
    @logAttribute2()
    public apiUrl:string | undefined;
    constructor(){
    }
​
    @logMethod1()
    @logMethod2()
    getData(){
        return true;
    }
​
    setData(@logParams1() attr1:any,@logParams2() attr2:any,){
​
    }
}
​
var http:any=new HttpClient();