美文网首页
TypeScript入门

TypeScript入门

作者: Betterthanyougo | 来源:发表于2020-05-05 22:33 被阅读0次

    TypeScript起步入门

    typeScript的存在,无疑就是为了更加规范化,是继ES5,ES6之后更加高级的一种javascript的超集。他无法被浏览器所识别,所以就要经过编译过后,编译成普通的es5语法。先从头来了解这玩意怎么用:
    1、当然是先下载 npm install -g typescript(不知道npm是什么的先去看node.js),下载完可以在命令行打tsc -v来看版本
    2、怎么编译呢? 我们可以用命令tsc index.ts这就能编译出es5语法的js,如果我们每写一点就要编译岂不是很麻烦,看下面:
    首先在我们项目文件夹输入 tsc --init ,之后会有一个config文件
    将outDir解开,后边是输出js的相对路径

    image.png

    TypeScript数据类型

    首先我们要知道常用的数据类型有哪些,平时我们在js中常用的也就bolean,string,number,undefined,null,arr,在ts中增加了几种数据类型,比如:元组,枚举,以及数组的不同使用的方法,下面一个一个来看:

    首先有一点,ts中语法要求很严格,比如你定义一个字符串类型的变量,就不能再为他赋值为数值类型。再者比如你使用了一个未定义的变量或者方法也会直接报错,确实很规范化啊
    1、字符串类型
    为变量定义基本数据类型的时候一把都是下面这种格式 变量:=""

    let a:string='my name is xiaoming'
    console.log(a)
    

    2、number类型

    let b:number=13
    // b='as' 报错 不能声明为其他类型的
    console.log(b)
    

    3、boolean类型

     let c:boolean=false
     c=true
     console.log(c)
    

    4、数组类型
    数组类型和普通的数据类型有些区别,有两种定义方法,这里如果定义了number类型,那么就无法再给数组赋值为字符串类型的元素

    let d:Array<number>=[1,2,3]
    d=[3,4,5]
    console.log(d)
    
    let f:number[]=[1,2,3]
    console.log(f)
    

    5、元组类型
    元组类型有些类似数组类型,是数组类型的扩展,需要注意的是这里定义了几个变量下边就只能写几个元素,如果不符合定义的规范,就会报错

    let e:[number,string]
    e=[1,'2']
    console.log(e[0])
    

    6、枚举类型
    枚举类型 enum 这里主要是为了定义一些固定值 比如状态值,产品固定特性等,下面是具体使用的方法。如果枚举对象没有被赋值,那么打印出来的就是该元素的下标。

    enum Color {Red='red', Green='green', Blue='blue'};
    let g: Color = Color.Blue;
    console.log(g);    // 输出 blue
    

    7、any类型
    any是个什么类型呢,他可以说是一个万能类型,就像原来什么都不写的时候,你无论给他什么类型,都不会出错,下面看看具体使用场景

    let msg:any='123'
    msg=true
    console.log(msg)
    

    可以看到他并没有报错,我们来看看下面的场景


    image.png

    如果你要获取dom元素,那么使用any再合适不过了
    8、undefined null类型

    let k:undefined
    console.log(k)
    let l:undefined
    l=2 // 这样就会报错
    //null同理
    let k:null
    k=2  //报错
    
    //来看看混合使用的方法
    let k:undefined | null | number
    k=undefined  //不报错
    k=null//不报错
    k=2//不报错
    

    9、void类型
    这是一个奇怪的类型,叫做不返回值类型,什么意思呢,可以简单的理解为这个函数没有做任何实际性的操作,比如下面这样:

    function a():void{
      console.log("a")
    }
    //像这样没有任何实际影响的方法就用void
    function b():number{
      return 123
    }
    

    //我们知道ts是非常严格的,会去检查你的代码是否有错,当你上边方法定义的返回数值类型,里边返回的是字符串类型,这样是会报错的。
    10、never类型
    这种类型主要针对那些无限循环或者抛出异常,无法执行到终点的事件

    
    let x: never;
    let y: number;
    
    // 运行错误,数字类型不能转为 never 类型
    x = 123;
    
    // 运行正确,never 类型可以赋值给 never类型
    x = (()=>{ throw new Error('exception')})();
    
    // 运行正确,never 类型可以赋值给 数字类型
    y = (()=>{ throw new Error('exception')})();
    
    // 返回值为 never 的函数可以是抛出异常的情况
    function error(message: string): never {
        throw new Error(message);
    }
    
    // 返回值为 never 的函数可以是无法被执行到的终止点的情况
    function loop(): never {
        while (true) {}
    }
    

    TypeScript函数

    在ts中不止定义变量需要写类型,在写函数中参数的时候也需要写类型,可能是为了更加规范化,而且让人看得第一眼就能明白。

    可选参数

    我们知道老方法写函数的时候,传参没什么具体要求,传两个形参,只有一个实参,或者传两个实参,只有一个形参,这都是可以的。但是在ts中,你必须传相对应的参数,比如定义了一个形参,你就只能传一个实参,或者定义了两个形参,你就不能只传一个实参,看下面例子

    function update(name:string,age?:number){
        console.log(name)
        console.log(age)
    }
    update('syx')   //age后边?代表可选参数,有问号代表可以传可以不传,如果没有就会报错了
    function update(name:string,age:number){
        console.log(name)
        console.log(age)
    }
    update('syx')   //这是错误示例
    

    默认参数

    默认参数和可选参数差不多,也是基于规范,传参必须对应。这里有默认age=18,不传参数也是可以的。如果没有默认参数,就会报错。

    function init(name:string,age:number=18){
        console.log(name)
        console.log(age)
    }
    init('syx')
    

    剩余参数

    剩余参数就像es6的参数,用...arg代表,只不过要写上类型

    function last(name:string,...arr:number[]){
        console.log(name)
        let count:number=0
        for(var i=0;i<arr.length;i++){
            count+=arr[i]
        }
        return count
    }
    alert(last('syx',1,2,3))
    

    箭头函数

    箭头函数完全就是es6的箭头函数,只是变量要加上类型

    var a=(x:number)=>{
        return x+10
        console.log(x)
    }
    a(10) //20
    

    函数的重载

    在js中我们知道函数是不能重载的,会覆盖原来定义的函数,在ts中可以重载,但是这里具体意义还没搞懂,后边再补上

    function freight(str:string):void
    function freight(str:number):void
    function freight(x:any,y?:any):void{
        console.log(x)
        console.log(y)
    }
    freight('syx')
    //这里定义了三个同名函数,在使用的时候回自动去匹配应该执行哪个函数。
    //转成js代码是这样的
    function freight(x, y) {
        console.log(x);
        console.log(y);
    }
    freight(123);
    

    Typescript接口

    接口用关键字interface来定义,这个接口可不是你平时项目前后端对接所谓的接口,可以理解为定义了一种规范,用来约束你所定义的参数,类,对象等等。

    传参约束

    对于传参约束,我觉得可以联想到平常项目开发中用到的一些传参规范,比如对接的时候,请求方法的传参,像下面这样在传参的时候将规范定义在后边:params:config

    interface config{
        interface:string;
        data?:string;  //问号代表可不传
        showLoading:boolean
    }
    
    function httpGet(params:config){
        if(params.showLoading){
            api.showLoading()
        }
        axios.get(params.interface,params.data,function(){
            
        })
    }
    //这样我们在传入参数时就必须按照这种规范来传
    httpGet({
        interface:'http://fy/getNewsList',
        showLoading:false,
    }) 
    

    上边定义的config接口,规定了必须要传interface,showLoading这两个参数,而且参数类型必须是string和boolean。data后边有个问号,可以是选填。
    这样你就可以在你所有的请求中只要有这种约束,就必须按照这种传参来传,保证了传入参数的正确性。

    函数类型接口

    这种接口可以用在你定义的方法中,用来约束这个方法的传入值以及返回值

    //函数类型接口  对方法传入的参数进行约束,以及返回值进行约束 ??? 
    interface enctypt{
        (key:string,value:string):string  //代表必须传入key value 返回值必须是string
    }
    
    var md5:enctypt=function(key:string,value:string):string{
        return key+value
    }
    var ssh:enctypt=function(key:string):string{
        return key
    }
    console.log(md5('张三','123456'))
    console.log(ssh('李四','456789')) 
    

    对数组,对象的约束

    [index:number]:string代表索引是数字,那么对应的就是数组,因为数组的下标才是数字,string代表这个索引对应的值。

     //可索引接口  对数组的约束
    
    interface UserArr{
        [index:number]:string
    }
    var arr:UserArr=['aaa','bbb']
    console.log(arr[0])
    
    //对对象的约束
    interface obj{
        [index:string]:string  //[index:string] 代表索引是字符串 代表这是一个对象 后边string代表值
    }
    var obj:obj={name:'asd',age:13} //错误 
    

    类类型接口

    类类型接口相当于先定义了一个接口,后边凡是想符合我这个接口的类,就必须按照我这种规范来做。这里用implements关键字来代表符合该接口规范,这种接口有点像类的多态,定义一个方法不去实现,让子类去实现这个方法。

    //这里Animal类规定了凡是要用我规范的 就必须有name,eat这两个属性和方法
    interface Animal{
        name:string;
        eat(name:string):void;
    }
    
    class Dog implements Animal{
        name:string;
        constructor(name:string){
            this.name=name
        }
        eat(){
            console.log(this.name+'吃骨头')
        }
    }
    class Cat implements Animal{
        name:string;
        constructor(name:string){
            this.name=name
        }
        eat(param:string){
            console.log(this.name+'吃'+param)
        }
    }
    var dog=new Dog('二哈')
    var cat =new Cat('杰瑞')
    dog.eat()
    cat.eat('老鼠')
    

    上述代码表示:构造了Dog和Cat两个类,要求是符合Animal这个接口的规范。可以看到每个类中都有name这个属性,eat这个方法,如果不定义,那么ts在解析时就会报错。

    Typescript继承

    ts实现构造函数用的是class,就是es6的语法,比如下面这样:

    一共分为五步:
    1、class构造一个类
    2、定义变量类型
    3、constructor将变量注册为类的变量,这里要注意类型要一致
    4、方法直接写在类里边,定义方法的返回类型
    5、实例化一个类,调用方法的时候直接用实例化的类去调用即可

    class Person{
        name:string;
        age:number
    
        constructor(name:string,age:number){
            this.name=name
            this.age=age
        } 
    
        getName():void{
            alert(this.name)
            alert(this.age)
        }
        setName(name:string):void{
            this.name=name
        }
    }
    var person = new Person('张三',18)
    alert(person.getName())
    

    ts实现继承用的是关键字 extends

    我们实现了一个Person类,这里我们实现一个student类想要继承Person类的方法,也是分为三步走:
    1、class 类名 extends 父类名
    2、construtor将父类的变量继承下来,这里用extends时候,父类的方法会自动注册到子类的身上,如果子类在一个定义了相同的方法,就会覆盖掉父类身上的这个方法
    3、实例化一个对象,调用对象身上的方法

    class Student extends Person{
        constructor(name:string,age:number){
            super(name,age)
        }
        getName():void{
            alert(`${this.name}是一个好孩子`)
        }
    }
    var student=new Student('李四',16)
    student.getName() //李四是一个好孩子
    

    类关键字 public private protected

    public:定义的变量可以在本身以及继承的子类中使用以及外部使用
    private 私有变量 只能在自己类中使用
    protected 保护变量 可以在本身及自己的子类中使用

    class A{
        protected name:string
    
        constructor(name:string){
            this.name=name
        }
    
        getName():string{
            return `${this.name}是共有的变量`
        }
    }
    
    class B extends A{
        constructor(name:string){
            super(name)
        }
        sayName():void{
            alert(this.name)  //在使用private的时候,这里会报错,因为private只能在本身使用
        }
    }
    var a=new A('小明')
    var b=new B('小刚')
    
    alert(a.name)  //protected 这里会报错,因为不是在本身也不是在他的子类中使用
    alert(b.name)
    

    实现静态方法

    首先在es5中实现静态方法我们使用下面这种方式,直接在构造的方法上加一个方法

    function Base(name){
        //这样在内部定义的就叫实例方法  调用需要先实例化一个对象 然后调用对象的方法
        this.name=name
        this.sayAge=function(){
            alert(this.name)
        }
    }
    //这样的就叫静态方法 调用可以直接 Base.getAge()
    Base.getAge=function(){
        alert(this.age)
    }
    

    在ts中使用static去定义静态变量以及静态方法,在调用的时候就可以不用通过实例化一个对象,而是直接去调用。

    class Per{
        public name:string;
        public age:number;
        static sex:string;
        constructor(name:string,age:number){
            this.name=name;
            this.age=age
        }
        getName():void{
            alert(this.name)
        }
        static getSex():void{
            alert(this.sex)
        }
    }
    //静态方法通过static定义 并且可以直接通过类去调用
    Per.getSex()
    

    多态

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

    //多态  父类定义的方法不去实现 让他的子类去实现 每一个子类有不同的表现
    
    class Animal{
        name:string;
        constructor(name:string){
            this.name=name
        }
    
        eat(){
    
        }
    }
    
    class Dog extends Animal{
        constructor(name:string){
            super(name)
        }
    
        eat(){
            alert(`${this.name}爱吃肉`)
        }
    }
    class Cat extends Animal{
        constructor(name:string){
            super(name)
        }
    
        eat(){
            alert(`${this.name}爱吃鱼`)
        }
    }
    var dog=new Dog('二哈')
    var cat=new Cat('杰斯')
    dog.eat()
    cat.eat()
    

    我们先定义了一个Animal类,在类中定义了一个eat方法,但是我们没有去实现这个方法,而是让子类去实现,每一个子类有不同的变现,从而实现了类的多态性

    抽象

    抽象类是给其他类提供基准的基类,不能被直接实例化
    抽象 anstract 用abstract关键字定义抽象类和抽象方法,抽象类中抽象方法不包含具体的实>现,必须在他的派生类中实现 换句话说 就是在子类中实现
    抽象方法只能放在抽象类中 凡是继承了抽象类的子类 必须包含父类中抽象的方法
    抽象就是为子类定义一个基准

    abstract class Animal{
        name:string;
        constructor(name:string){
            this.name=name
        }
        abstract eat():any
    }
    // var animal=new Animal()  //不能创建抽象类的实例
    class Dog extends Animal{
        constructor(name:string){
            super(name)
        }
    
        eat(){
            alert(`${this.name}爱吃肉`)
        }
    }
    class Cat extends Animal{
        constructor(name:string){
            super(name)
        }
        eat(){
            alert(`${this.name}爱吃鱼`)
        }
    } 
    
    

    这里如果子类中没有实现父类的抽象方法,就会报错,并提示抽象类不能被继承,所以我们在写字类继承抽象类的时候,必须实现父类中的抽象方法。

    image

    Typescript泛型

    泛型:泛型就是解决 类 接口 方法的复用性、以及对不特定数据类型的支持
    实现方法:function method<T>(value:T):T{} 这个T只是用法最多的,但不是写死的,你想用A,B什么来代替也一样
    为什么说解决复用性?大家想一下,如果我们要定义一个方法,但是要约束传入的参数和返回的类型一致,也就是说,我传入参数1,要返回number类型,传入‘张三’,要返回string类型,那么现在是不是要写两个方法分别来约束,这就造成了冗余,为了解决这个问题,我们可以使用泛型来定义约束

    1、泛型函数

    //旧方法约束传参和返回类型保持一致
    function getData(value:number):Number{
        return value
    }
    function getData_c(value:string):string{
        return value
    }
    //泛型解决传参和返回类型一致
    function getData<T>(value:T):T{
        return value
    }
    console.log(getData<number>(123)) //<number>传入number 那么value必须为number 传入string时就会报错
    console.log(getData<string>('123'))
    

    2、泛型类
    实现一个类 来找到数组中的最小值,数组元素类型不定

    class minCount{
        public list:number[]=[];
    
        add(value:number):void{
            this.list.push(value)
        }
    
        min():number{
            let minNum=this.list[0]
            for(var i=0;i<this.list.length;i++){
                if(this.list[i]<minNum){
                    minNum=this.list[i]
                }
            }
            return minNum
        }
    }
    var a=new minCount()
    a.add(1)
    a.add(2)
    a.add(3)
    alert(a.min())
    

    这种方法弊端:辛辛苦苦写了一个类,最终只能比较数字类型,要是想比较字符串类型还要重构,这也太麻烦了,下面看改造成泛型的类

    class minCount<T>{
        public list:T[]=[]
    
        add(value:T){
            this.list.push(value)
        }
    
        min():T{   
            let minNum=this.list[0]
            for(var i=0;i<this.list.length;i++){
                if(this.list[i]<minNum){
                    minNum=this.list[i]
                }
            }
            return minNum
        }
    }
    var a=new minCount<number>()
    a.add(1)
    a.add(2)
    a.add(3)
    // a.add('1') //错误 不能赋值给number类型
    var b=new minCount<string>()
    b.add('a')
    b.add('b')
    b.add('c')
    

    看下泛型都是用在哪些地方
    1、类的后边
    2、数组类型
    3、add方法的传参
    4、min方法的返回值

    这四处既能够很好的保证你传入的参数和返回参数一致,又能降低冗余,起到了限制的作用和不确定数据类型的约束
    3、泛型接口

    interface config{
        <T>(value:T):T;
    }
    
    var getYear:config=function<T>(value:T):T{
        return value
    }
    
    var c=getYear<string>('张三')  //正确
    // var c=getYear<number>('张三')  //错误
    

    4、类作为参数约束传入数据的类型
    这个举例子来说可能更明了一些:假设你想定义一个方法,这个方法是往数据库传入数据的,但是你要做一个约束,不能让非法类型的数据传入数据库。
    比如定义一个用户类,要求传入用户名和密码,这时候如果传入的是性别或者其他属性,肯定是不行的。这时候就要用到类作为参数来约束传入数据的类型,可以理解为接口的特性,就是起到约束的作用。
    然后你还有一个文章类,你要传入title,desc,type等属性,这时候如果传入的参数是年龄,大小这些属性肯定也是不行的,这时候你就要用到文章类来约束传入的参数。下面看具体的例子

    class User{
        username:string | undefined;
        password:string | undefined;
    }
    
    class Artical{
        title:string | undefined;
        desc:string | undefined;
        type:string | undefined
    }
    
    class mysqlDB{
        //定义一个方法 传入的参数要遵从User类 
        add(value:User):boolean{
            console.log(value)
            return true
        }
    }
    
    //这样定义的类如果想要约束多个类的规范 就必须重复的写多个 这时候使用泛型 就可以避免重复了
    class mysqlDB_c{
        //定义一个方法 传入的参数要遵从User类 
        add(value:Artical):boolean{
            console.log(value)
            return true
        }
    }
    
    var u=new User()
    // u.username='张三'
    u.password='123445'
    var params={
        name:'09'
    }
    
    var DB=new mysqlDB()
    var DB_C=new mysqlDB_C()
    // DB.add(params)  //错误 这里的params不符合user类的规范
    // DB.add(u)
    
    var artical=new Artical()
    artical.title='学习'
    artical.desc='学习让人进步'
    
    DB_C.add(artical)
    

    这样虽然可以实现约束效果,但是如果还有更多的类约束,那么岂不是要写很多的mysqlDB类,这样显然不可行,下面看使用泛型来解决这个问题。

    class User{
        username:string  | undefined;
        password:string | undefined;
    }
    
    class Artical{
        title:string | undefined;
        desc:string | undefined;
        type:string | undefined;
        constructor(params:{  //这里再次进行传入参数约束 必须穿入title,desc type 可不传
            title:string | undefined;
            desc:string | undefined;
            type?:string | undefined;  
        }){
            this.title=params.title,
            this.desc=params.desc,
            this.type=params.type
        }
    }
    
    //这样定义一个泛型类 在实例化的时候可以传入约束类 避免了重复构造mysqlDB类
    class mysqlDB<T>{
        add(value:T):boolean{
            console.log(value)
            return true
        }
    
        update(value:T,id:number):boolean{
            console.log(value,id)
            return true
        }
    }
    var u=new User()
    u.username='张三'
    
    var a=new Artical({
        title:'好好学习',
        desc:'天天向上'
    })
    
    var db=new mysqlDB<User>()
    db.add(u)
    
    var db1=new mysqlDB<Artical>()
    db1.add(a)
    db1.update(a,12)
    

    这里定义user类和artical类的时候有些区别,artical类直接在内部实现了constructor,这样就可以直接在实例化的时候传参,而且可以看到artical类在构造的时候也是进行了约束的,这样约束之后在传参的时候就必须传入title,desc参数,type参数是可选的

    mysqlDB类在定义的时候使用了泛型的方法,<T>确保了实例化对象的时候使用了哪种类约束。使用了哪种规范就必须按照这种方式来传参了。

    相关文章

      网友评论

          本文标题:TypeScript入门

          本文链接:https://www.haomeiwen.com/subject/kifbghtx.html