笔记:12:TypeScript

菜鸟教程:https://www.runoob.com/typescript/ts-basic-syntax.html

目录

1、安装
2、操作
3、配置自动编译
4、typeScript中的数据类型
5、typeScript中的数据类型简介
6、typeScript中的函数
7、es5创建对象继承
8、typeScript中的类
9、typeScript中的接口
10、typeScript中的泛型
11、例子
12、模块
13、命名空间
14、装饰器
- 报错

正文

1、安装

npm install -g typescript
// 验证是否安装成功
tsc -v

2、操作

  • 创建test.ts文件
var message:string = "Hello World" 
console.log(message)
  • 执行以下命令将 TypeScript 转换为 JavaScript 代码
tsc app.ts

3、配置自动编译

webStorm配置自动编译
webStorm配置自动编译ts
vscode配置自动编译
  • 第一步 tsc --init 生成tsconfig.json 改 "outDir": "./js",
  • 第二步 任务 - 运行任务 监视tsconfig.json

4、typeScript中的数据类型

typescript中为了使编写的代码更规范,更有利于维护,增加了类型校验,在typescript中主要给我们提供了以下数据类型。

  • 布尔类型(boolean)
  • 数字类型(number)
  • 字符串类型(string)
  • 数组类型(array)
  • 元组类型(tuple)
  • 枚举类型(enum)
  • 任意类型(any)
  • null 和 undefined
  • void类型
  • never类型

5、typeScript中的数据类型简介

  • ts代码须指定类型
    typescript中为了使编写的代码更规范,更有利于维护,增加了类型校验。写ts代码必须指定类型。
  • 布尔类型(boolean)
var flag:boolean=true;
// flag=123;  //错误
flag=false;  //正确 
console.log(flag);
  • 数字类型(number)
var a:number = 123
console.log(a)

// a = 'string' // 错误写法
// a = false // 错误写法
a = 12.3
console.log(a)
  • 字符串类型(string)
let str:string = 'hahaha'
// str = 12 // 错误写法
str = '你好'
  • 数组类型(array):ts中定义数组有两种方式
1.第一种定义数组的方式
var arr:number[]=[11,22,33];
console.log(arr);
let str:string[] = ['haha', 'hehe']
2.第二种定义数组的方式
var arr:Array<number>=[11,22,33];
console.log(arr)
3、第三种
var arr3:any[]=['131214',22,true];
console.log(arr3);
  • 元组类型(tuple) 属于数组的一种
var arr:Array<number>=[11,22,33];
console.log(arr)

元祖类型
let arr:[number,string]=[123,'this is ts'];
console.log(arr);
  • 枚举类型(enum)

随着计算机的不断普及,程序不仅只用于数值计算,还更广泛地用于处理非数值的数据。
例如:性别、月份、星期几、颜色、单位名、学历、职业等,都不是数值数据。
在其它程序设计语言中,一般用一个数值来代表某一状态,这种处理方法不直观,易读性差。
如果能在程序中用自然语言中有相应含义的单词来代表某一状态,则程序就很容易阅读和理解。
也就是说,事先考虑到某一变量可能取的值,尽量用自然语言中含义清楚的单词来表示它的每一个值,
这种方法称为枚举方法,用这种方法定义的类型称枚举类型。

enum 枚举名{ 
     标识符[=整型常数], 
     标识符[=整型常数], 
     ... 
     标识符[=整型常数], 
} ;

flag    1:表示true;-1:表示false

enum Flag {success=1, error=-1}
var f:Flag = Flag.error
console.log(f)
console.log(Flag.error)

enum Color {red, blue=5, orange}
var c:Color = Color.red // 0
var c:Color = Color.blue // 5
var c:Color = Color.orange // 6
  • 任意类型(any)
var num:any=123;
num='str';
num=true;
console.log(num)
----------任意类型的用处
var oBox:any=document.getElementById('box');
oBox.style.color='red';
  • null 和 undefined:其他(never类型)数据类型的子类型。
var num:number;
console.log(num)  // 输出:undefined   报错

var num:undefined;
console.log(num)  //输出:undefined  //正确

var num:number | undefined;
num=123;
console.log(num);

//定义没有赋值就是undefined
var num:number | undefined;
console.log(num);

var num:null;
num=null;

//一个元素可能是 number类型 可能是null 可能是undefined
var num:number | null | undefined;
num=1234;
console.log(num)
  • void类型:typescript中的void表示没有任何类型,一般用于定义方法的时候方法没有返回值。
// 正确写法
function run():void{
    console.log('run')
}
run();

//错误写法
function run():undefined{
    console.log('run')
}
run();

//正确写法
function run():number{
   return 123;
}
run();
  • never类型:是其他类型 (包括 null 和 undefined)的子类型,代表从不会出现的值。这意味着声明never的变量只能被never类型所赋值。
var a:undefined;
a=undefined;

var b:null;
b=null;

var a:never;
// a=123; //错误的写法
// 正确写法,一般用不到
a=(()=>{
    throw new Error('错误');
})()

6、typeScript中的函数

(1)函数的定义

******es5定义函数的方法
// 函数声明法
function run(){

    return 'run';
}
// 匿名函数
var run2=function(){
    return 'run2';
}
******ts中定义函数的方法
// 函数声明法
function run():string{
    return 'run';
}
错误写法
function run():string{
    return 123;
}
// 匿名函数
var fun2=function():number{
    return 123;
}
alert(fun2()); /*调用方法*/

******ts中定义方法传参
function getInfo(name:string,age:number):string{
        return `${name} --- ${age}`;
}
alert(getInfo('zhangsan',20));

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

// 没有返回值的方法
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))

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

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

(3)默认参数(也称可选参数)

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

(4)剩余参数

function sum(a:number,b:number,c:number,d:number):number{
    return a+b+c+d;
}
alert(sum(1,2,3,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)) ;

function sum(a:number,b:number,...result:number[]):number{
    var sum=a+b;
    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){
}

// ts中的重载
function getInfo(name:string):string;
function getInfo(age:number):number;
function getInfo(str:any):any{
    if(typeof str==='string'){
        return '我叫:'+str;
    }else{
        return '我的年龄是'+str;
    }
}
alert(getInfo('张三'));   //正确
alert(getInfo(20));   //正确
alert(getInfo(true));    //错误写法

// 可选参数
function getInfo(name:string):string;
function getInfo(name:string,age:number):string;
function getInfo(name:any,age?:any):any{
    if(age){
        return '我叫:'+name+'我的年龄是'+age;
    }else{
        return '我叫:'+name;
    }
}
// alert(getInfo('zhangsan'));  /*正确*/
// alert(getInfo(123));  // 错误
// alert(getInfo('zhangsan',20));

(6)箭头函数(es6)

!!!this指向的问题:箭头函数里面的this指向上下文
// es5
setTimeout(function(){
    alert('run')
},1000)

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

7、es5创建对象继承

(1)最简单的类

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

(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)类里面的静态方法:直接调用;实例方法:需要new才能调用

function Person(){
    this.name='张三';  /*属性*/
    this.age=20;
    this.run=function(){  /*实例方法:需要new才能调用*/
        alert(this.name+'在运动');
    }
}

Person.getInfo=function(){
    alert('我是静态方法');
}
//原型链上面的属性会被多个实例共享   构造函数不会
Person.prototype.sex="男";
Person.prototype.work=function(){
    alert(this.name+'在工作');

}
var p=new Person();    
p.work();

//调用静态方法
Person.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(); // undefined在运动

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

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

8、typeScript中的类

(1)类的定义

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()
---------------------------------------------------------------------
    class Person{
        name:string; 
        constructor(name:string){  //构造函数   实例化类的时候触发的方法
            this.name=name;
        }
        getName():string{
            return this.name;
        }
        setName(name:string):void{
            this.name=name;
        }
    }
    var p=new Person('张三');
    alert(p.getName());

    p.setName('李四');
    alert(p.getName());

(2)ts中实现继承:extends、super

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中继承的探讨:父类的方法和子类的方法一致
    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);  /*初始化父类的构造函数*/
        }
        run():string{
            return `${this.name}在运动-子类`
        }
        work(){
            alert(`${this.name}在工作`)
        }
    }
    var w=new Web('李四');
    alert(w.run());
    w.work();
    alert(w.run());

(3)类里面的修饰符:typescript里面定义属性的时候给我们提供了三种修饰符。

  • public:公有。在当前类里面、子类、类外面都可以访问。
  • protected:保护类型。在当前类里面、子类里面可以访问,在类外部没法访问。
  • private :私有。在当前类里面可以访问,子类、类外部都没法访问。
    !!!属性如果不加修饰符,默认就是:公有(public)。
1、public:公有。在类里面、子类、类外面都可以访问。
    class Person{
        public 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);  /*初始化父类的构造函数*/
        }
        run():string{
            return `${this.name}在运动-子类`
        }
        work(){
            alert(`${this.name}在工作`)
        }
    }
    var w=new Web('李四');
    w.work();
    ---------------------------------
    类外部访问公有属性
    class Person{
        public name:string;  /*公有属性*/
        constructor(name:string){
            this.name=name;
        }
        run():string{
            return `${this.name}在运动`
        }
    }
    var  p=new Person('哈哈哈');
    alert(p.name);
*******************************************************************************
2、protected:保护类型。在类里面、子类里面可以访问,在类外部没法访问。
    class Person{
        protected 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);  /*初始化父类的构造函数*/
        }                  
        work(){
            alert(`${this.name}在工作`)
        }
    }
    var w=new Web('李四11');
    w.work();
    alert( w.run());
    ----------------------------
    类外外部没法访问保护类型的属性
    class Person{
        protected name:string;  /*保护类型*/
        constructor(name:string){
            this.name=name;
        }
        run():string{
            return `${this.name}在运动`
        }
    }
    var p=new Person('哈哈哈');
    alert(p.name);
*******************************************************************************
3、private:私有。在类里面可以访问,子类、类外部都没法访问。
    class Person{
        private name:string;  /*私有*/
        constructor(name:string){
            this.name=name;
        }
        run():string{
            return `${this.name}在运动`
        }
    }
    var p=new Person('哈哈哈');
    alert(p.run()); // 访问不到

(4)静态属性、静态方法

function Person(){
    this.run1=function(){ // 实例方法
    }
}
Person.name='哈哈哈';
Person.run2=function(){  // 静态方法
}
var p=new Person();
Person.run2(); // 静态方法的调用
-------------------------------------------------------
例子:jQuery
    function $(element){
        return new Base(element)
    }
    $.get=function(){
    }
    function Base(element){
        this.element=获取dom节点;
        this.css=function(arr,value){
            this.element.style.arr=value;
        }
    }
    $('#box').css('color','red')
    $.get('url',function(){
    })
**********************************************
    class Per{
        public name:string;
        public age:number=20;
        //静态属性
        static sex="男";
        constructor(name:string) {
                this.name=name;
        }
        run(){  /*实例方法*/
            alert(`${this.name}在运动`)
        }
        work(){
            alert(`${this.name}在工作`)
        }
        static print(){  /*静态方法:里面没法通过this来直接调用类里面的属性*/
            alert('print方法'+Per.sex);
        }
    }
    // var p=new Per('张三');
    // p.run();
    Per.print();
    alert(Per.sex);

(5)抽象类:多态:父类定义一个方法不去实现,让继承它的子类去实现,每一个子类有不同的表现。多态属于继承。

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+'吃老鼠'
    }
}
  • 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();

class Cat extends Animal{
    //抽象类的子类必须实现抽象类里面的抽象方法
    constructor(name:any){
        super(name)
    }
    run(){
    }
    eat(){
        console.log(this.name+'吃老鼠')
    }
}
var c=new Cat('小花猫');
c.eat();

9、typeScript中的接口

接口的作用:在面向对象的编程中,接口是一种规范的定义,它定义了行为和动作的规范,在程序设计里面,接口起到一种限制和规范的作用。接口定义了某一批类所需要遵守的规范,接口不关心这些类的内部状态数据,也不关心这些类里方法的实现细节,它只规定这批类里必须提供某些方法,提供这些方法的类就可以满足实际需要。
用来定义标准。

(1)属性类接口:对json的约束。

ts中定义方法
    function printLabel():void {
        console.log('printLabel');
    }
    printLabel();
ts中定义方法传入参数
    function printLabel(label:string):void {
        console.log('printLabel');
    }
    printLabel('hahah');
ts中自定义方法传入参数,对json进行约束
    function printLabel(labelInfo:{label:string}):void {
        console.log('printLabel');
    }
    printLabel('hahah'); //错误写法
    printLabel({name:'张三'});  //错误的写法
    printLabel({label:'张三'});  //正确的写法
*************************************************************
接口:行为和动作的规范,对批量方法传入参数进行约束。
    就是传入对象的约束、属性接口
    interface FullName{
        firstName:string;   //注意;结束
        secondName:string;
    }
    function printName(name:FullName){
        // 必须传入对象  firstName  secondName
        console.log(name.firstName+'--'+name.secondName);
    }
    // printName('1213');  //错误
    var obj={   /*传入的参数必须包含 firstName  secondName*/
        age:20,
        firstName:'张',
        secondName:'三'
    };
    printName(obj)
    -----------------------------------------------------------------
    interface FullName{
        firstName:string;   //注意;结束
        secondName:string;
    }

    function printName(name:FullName){
        // 必须传入对象  firstName  secondName
        console.log(name.firstName+'--'+name.secondName);
    }
    function printInfo(info:FullName){
        // 必须传入对象  firstName  secondName
        console.log(info.firstName+info.secondName);
    }

    var obj={   /*传入的参数必须包含 firstName  secondName*/
        age:20,
        firstName:'张',
        secondName:'三'
    };
    printName(obj);
    printInfo({
        firstName:'李',
        secondName:'四'
    })
接口 :参数顺序
    interface FullName{
        firstName:string;
        secondName:string;
    }

    function getName(name:FullName){
        console.log(name)
    }
    //参数的顺序可以不一样
    getName({        
        secondName:'secondName',
        firstName:'firstName'
    })
接口 :可选属性
    interface FullName{
        firstName:string;
        secondName?:string; // ?:可选属性
    }

    function getName(name:FullName){
        console.log(name)
    }  
    getName({               
        firstName:'firstName'
    })
****************************************************************
例子
    //原生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('chengong');
                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', //api
        dataType:'json'
    })

(2)函数类型接口:对方法传入的参数,以及返回值进行约束。批量约束。

例子:加密的函数类型接口
    interface encrypt{
        (key:string,value:string):string; // 不需要写function
    }

    var md5:encrypt=function(key:string,value:string):string{
        //模拟操作
        return key+value;
    }
    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','222']
******************************************
可索引接口:对数组的约束。
    interface UserArr{
        [index:number]:string
    }

    var arr:UserArr=['aaa','bbb'];
    console.log(arr[0]);

    var arr:UserArr=[123,'bbb'];  /*错误*/
    console.log(arr[0]); /*错误*/
可索引接口:对对象的约束。
    interface UserObj{
        [index:string]:string
    }

    var arr:UserObj={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 d=new Dog('小黑');
    d.eat();

    class Cat implements Animal{
        name:string;
        constructor(name:string){
            this.name=name;
        }
        eat(food:string){
            console.log(this.name+'吃'+food);
        }
    }
    var c=new Cat('小花');
    c.eat('老鼠');

(5)接口扩展:接口可以继承接口

    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 w=new Web('小李');
    w.eat();
    ********************************************************
    
    interface Animal{
        eat():void;
    }
    interface Person extends Animal{
        work():void;
    }
    class Programmer{
        public name:string;
        constructor(name:string){
            this.name=name;
        }
        coding(code:string){
            console.log(this.name+code)
        }
    }
    class Web extends Programmer implements Person{ // 继承+实现
        constructor(name:string){
           super(name)
        }
        eat(){
            console.log(this.name+'喜欢吃馒头')
        }
        work(){
            console.log(this.name+'写代码');
        }
    }
    var w=new Web('小李');
    // w.eat();
    w.coding('写ts代码'); // 继承来的

10、typeScript中的泛型

(1)泛型定义

  • 泛型:软件工程中,我们不仅要创建一致的定义良好的API,同时也要考虑可重用性。组件不仅能够支持当前的数据类型,同时也能支持未来的数据类型,这在创建大型系统时为你提供了十分灵活的功能。
  • 在像C#和Java这样的语言中,可以使用泛型来创建可重用的组件,一个组件可以支持多种类型的数据。 这样用户就可以以自己的数据类型来使用组件。
  • 通俗理解:泛型就是解决:类、接口、方法的复用性、以及对不特定数据类型的支持(类型校验)。
只能返回string类型的数据
    function getData(value:string):string{
        return value;
    }
同时返回 string类型 和number类型  (代码冗余)
    function getData1(value:string):string{
        return value;
    }
    function getData2(value:number):number{
        return value;
    }
同时返回 string类型 和number类型。any可以解决这个问题。
    function getData(value:any):any{
        return '哈哈哈';
    }
    getData(123);
    getData('str');
!!!any放弃了类型检查,传入什么,返回什么。比如:传入number 类型必须返回number类型。传入string类型必须返回string类型。
传入的参数类型和返回的参数类型可以不一致
    function getData(value:any):any{
        return '哈哈哈';
    }

(2)泛型函数

  • 泛型:可以支持不特定的数据类型。要求:传入的参数和返回的参数一致。
  • T表示泛型,具体什么类型是调用这个方法的时候决定的。
function getData<T>(value:T):T{
    return value;
}
getData<number>(123);
getData<string>('1214231');
getData<number>('2112');       /*错误的写法*/  
--------------------------------------------
function getData<T>(value:T):any{
    return '2145214214';
}
getData<number>(123);  //参数必须是number
getData<string>('这是一个泛型');

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

class MinClass{
    public list:number[]=[];
    add(num:number){
        this.list.push(num)
    }
    min():number{
        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 m=new MinClass();

m.add(3);
m.add(22);
m.add(23);
m.add(6);
m.add(7);
alert(m.min());
  • 类的泛型
class MinClas<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 MinClas<number>();   /*实例化类 并且制定了类的T代表的类型是number*/
m1.add(11);
m1.add(3);
m1.add(2);
alert(m1.min())

var m2=new MinClas<string>();   /*实例化类 并且制定了类的T代表的类型是string*/
m2.add('c');
m2.add('a');
m2.add('v');
alert(m2.min())
  • 定义一个User的类,这个类的作用就是映射数据库字段,然后定义一个 MysqlDb的类这个类用于操作数据库,然后把User类作为参数传入到MysqlDb中。
var user=new User({
    username:'张三',
    password:'123456'
})
var Db=new MysqlDb();
Db.add(user);
  • (例子1)问题:代码重复
// 把类作为参数来约束数据传入的类型 
class User{
    username:string | undefined;
    pasword:string | undefined;
}
class MysqlDb{
    add(user:User):boolean{
        console.log(user);
        return true;
    }
}
var u=new User();
u.username='张三';
u.pasword='123456';
var Db=new MysqlDb();
Db.add(u);
--------------------------------------------------------------
class ArticleCate{
    title:string | undefined;
    desc:string | undefined;
    status:number | undefined
}
class MysqlDb{
    add(info:ArticleCate):boolean{
        console.log(info);
        console.log(info.title);
        return true;
    }
}
var a=new ArticleCate();
a.title="国内";
a.desc="国内新闻";
a.status=1;
var Db=new MysqlDb();
Db.add(a);
  • (例子2)
定义操作数据库的泛型类
    class MysqlDb<T>{
        add(info:T):boolean{
            console.log(info);       
            return true;
        }
        updated(info:T,id:number):boolean {
            console.log(info);  
            
            console.log(id); 

            return true;
        }
    }
*********************************************************************
1、想给User表增加数据,定义一个User类,和数据库进行映射。
    class User{
        username:string | undefined;
        pasword:string | undefined;
    }
    var u=new User();
    u.username='张三';
    u.pasword='123456';
    var Db=new MysqlDb<User>();
    Db.add(u);
------------------------------------------------------------------------
2、通过ArticleCate增加数据,定义一个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.desc=params.desc;
            this.status=params.status;
        }
    }
    增加操作
        var a=new ArticleCate({
            title:'分类',
            desc:'1111',
            status:1
        });
        //类当做参数的泛型类
        var Db=new MysqlDb<ArticleCate>();
        Db.add(a);

    修改数据
        var a=new ArticleCate({ // 实例化传值
            title:'分类111',
            desc:'2222'
        });
        a.status=0;
        var Db=new MysqlDb<ArticleCate>();
        Db.updated(a,12);

(4)泛型接口

函数类型接口
    interface ConfigFn{
        (value1:string,value2:string):string;
    }
    var setData:ConfigFn = function(value1:string,value2:string):string{
        return value1+value2;
    }
    setData('name','张三');
1、泛型接口
    interface ConfigFn{
        <T>(value:T):T;
    }

    var getData:ConfigFn=function<T>(value:T):T{
        return value;
    }
    // getData<string>('张三');
    // getData<string>(1243);  //错误
2、泛型接口
    interface ConfigFn<T>{
        (value:T):T;
    }
    function getData<T>(value:T):T{
        return value;
    }        
    var myGetData:ConfigFn<string>=getData; // myGetData实现ConfigFn这个泛型
    myGetData('20');  /*正确*/
    // myGetData(20)  //错误

11、例子

  • 功能:定义一个操作数据库的库 支持 Mysql Mssql MongoDb
  • 要求1:Mysql MsSql MongoDb功能一样 都有 add update delete get方法
  • 注意:约束统一的规范、以及代码重用
  • 解决方案:需要约束规范所以要定义接口 ,需要代码重用所以用到泛型
    1、接口:在面向对象的编程中,接口是一种规范的定义,它定义了行为和动作的规范
    2、泛型 通俗理解:泛型就是解决 类 接口 方法的复用性
标准
    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('数据库建立连接');
        }
        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;
        }
    }
---------------------------------------------------------------------------
定义一个操作mssql数据库的类
    class MsSqlDb<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;
        }
    }
***********************************************************
操作用户表:定义一个User类和数据表做映射
    class User{
        username:string | undefined;
        password:string | undefined;
    }
    var u=new User();
    u.username='张三111';
    u.password='123456';
    var oMysql=new MysqlDb<User>(); //类作为参数来约束数据传入的类型 
    oMysql.add(u);
    ------------------------------------------------------------------------------
    class User{
        username:string | undefined;
        password:string | undefined;
    }
    var u=new User();
    u.username='张三2222';
    u.password='123456';
    var oMssql=new MsSqlDb<User>();
    oMssql.add(u);
    //获取User表 ID=4的数据
    var data=oMssql.get(4);
    console.log(data);

12、模块

(1)概念

  • 官方
关于术语的一点说明: 请务必注意一点,TypeScript 1.5里术语名已经发生了变化。 “内部模块”现在称做“命名空间”。
“外部模块”现在则简称为“模块” 模块在其自身的作用域里执行,而不是在全局作用域里;
这意味着定义在一个模块里的变量,函数,类等等在模块外部是不可见的,除非你明确地使用export形式之一导出它们。 
相反,如果想使用其它模块导出的变量,函数,类,接口等的时候,你必须要导入它们,可以使用 import形式之一。
  • 自己理解
我们可以把一些公共的功能单独抽离成一个文件作为一个模块。
模块里面的变量 函数 类等默认是私有的,如果我们要在外部访问模块里面的数据(变量、函数、类),
我们需要通过export暴露模块里面的数据(变量、函数、类...)。
暴露后我们通过 import 引入模块就可以使用模块里面暴露的数据(变量、函数、类...)。

13、命名空间

  • 在代码量较大的情况下,为了避免各种变量命名相冲突,可将相似功能的函数、类、接口等放置到命名空间内。
  • 同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} 吃猫粮。`);
        }
    }   
}
var d = new A.Dog('狼狗')
d.eat()
-------------------------------------------------------------------------------
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} 在吃狗粮。`);
        }
    }
    export class Cat implements Animal {
        name: string;
        constructor(theName: string) {
            this.name = theName;
        }
        eat() {
            console.log(`${this.name} 在吃猫粮。`);
        }
    }   
}
var c=new B.Cat('小花');
c.eat();

14、装饰器

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

(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();
  • 类装饰器:装饰器工厂(可传参)
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);
  • 例子
下面是一个重载构造函数的例子。
类装饰器表达式会在运行时当作函数被调用,类的构造函数作为其唯一的参数。
如果类装饰器返回一个值,它会使用提供的构造函数来替换类的声明。
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个参数:

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

(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='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();
---------------------------------------------------------------------
方法装饰器二
    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个参数:

  • 对于静态成员来说是类的构造函数,对于实例成员是类的原型对象。
  • 方法的名字。
  • 参数在函数参数列表中的索引。
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 HttpClient{  
    public url:any |undefined;
    constructor(){
    }           
    getData(@logParams('xxxxx') uuid:any){
        console.log(299299, uuid);
    }
}
var http:any = new HttpClient();
http.getData(123456);
console.log( http.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();
image.png

- 报错

1、Element implicitly has an ‘any’ type because expression of type ‘string’ can’t be used to index type ‘{ name: string; age: number; }’.
No index signature with a parameter of type ‘string’ was found on type ‘{ name: string; age: number; }’.ts(7053)

// tsconfig.json
{
    "compilerOptions": {
        "suppressImplicitAnyIndexErrors": true,
        ...
    },
    ...
}

参考文件: TypeScript踩坑记录
参考文件:JavaScript 和 TypeScript 交叉口 —— 类型定义文件(*.d.ts)

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 193,968评论 5 459
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 81,682评论 2 371
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 141,254评论 0 319
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 52,074评论 1 263
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 60,964评论 4 355
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 46,055评论 1 272
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 36,484评论 3 381
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 35,170评论 0 253
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 39,433评论 1 290
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 34,512评论 2 308
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 36,296评论 1 325
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 32,184评论 3 312
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 37,545评论 3 298
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 28,880评论 0 17
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 30,150评论 1 250
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 41,437评论 2 341
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 40,630评论 2 335

推荐阅读更多精彩内容