typecript 入门笔记(1)

394 阅读11分钟

一,简介

  • TypeScript 是 JavaScript 的一个超集,支持 ECMAScript 6 标准。
  • TypeScript 由微软开发的自由和开源的编程语言。
  • TypeScript 设计目标是开发大型应用,它可以编译成纯 JavaScript,编译出来的 JavaScript 可以运行在任何浏览器上。
  • TypeScript 扩展了 JavaScript 的语法,因此现有的 JavaScript 代码可与 TypeScript 一起工作无需任何修改,TypeScript 通过类型注解提供编译时的静态类型检查。TypeScript 可处理已有的 JavaScript 代码,并只对其中的 TypeScript 代码进行编译

说了这么多,其实get到的信息,typescript是javascript的一个超集。但是,通过typecript学习,我们还知道:typescript 在完整保留 JavaScript 运行时行为的基础上,通过引入静态类型系统来提高代码的可维护性,减少可能出现的 bug。

二,主要内容

  • 下面我用思维导图,绘制typescript的主要内容(因人而异哈)

typescript.png

三,起步

  • 1,类型系统 因为typescript的一大特点就是:类型。相比于javascript来说,它多了类型约束。这样做的目的,是为了在运行前就对代码进行检测,保证了代码的质量性,可维护性等。

  • 2,静态类型 JavaScript是动态类型,因为是在运行时,才会对语法进行检测,如果有错误时,卡在当前错误处,阻止下面的代码运行。 typescript则不同,它在编译阶段就开始对语法进行检测,如果有错误,马上就提示。这就是静态类型。

  • 3,弱类型 我们知道,JavaScript是弱类型语音,其实typescript是完全兼容JavaScript语言的。它不会修改JavaScript运行时的特性。因此也属于弱类型。

  • 4,核心设计理念 在完整保留 JavaScript 运行时行为的基础上,通过引入静态类型系统来提高代码的可维护性,减少可能出现的 bug。

  • 5,安装

   npm install -g typescript
   或者 yarn add -g typescript
   
   //检测是否安装成功:
    tsc -v

  • 6,第一个typescript程序

hello-ts-1.jpg 这是因为 TypeScript 只会在编译时对类型进行静态检查,如果发现有错误,编译的时候就会报错。而在运行时,与普通的 JavaScript 文件一样,不会对类型进行检查。

但是 TypeScript 编译的时候即使报错了,还是会生成编译结果,我们仍然可以看到编译后的js文件,也没有语法错误。这个可以打印一下对应的js文件,即可验证。

  • 7,关于运行typescript代码(不用webpack等工具),关于vscode运行typescript可参考

四,基础

  • 1,原始数据类型

    • boolean
    let isOk:boolean = false
     //编译通过
    let isFine:boolean = new Boolean(0)  
    //编译不通过,这是因为:使用构造函数Boolean 创造的对象不是 布尔值
    // new Boolean()返回的是一个对象
    
    • number
    let n1:number = 123
    let n2:number = NaN
    
    //编译通过 结果为
    var n1 = 123
    var n2 = NaN
    
    • string
    let name:string =  'zhangsan'
    let age:string = 24
    
    //编译 name通过,但是在age,不通过。因为类型约束为 string
    
    //编译结果都是正确的  在开始我们也讨论过  详情查看hello typescript介绍
    var name = 'zhangsan'
    var age = 24
    
    • null 和 undefined typescript中,null 和 undefined来定义原始数据类型。也就是说,undefined 和 null 是所有类型的子类型。
     let a1:undefined = undefined
     let a2:null = null
    
     // 可以赋值给 string类型的变量:
     let str1:string = undefined   //编译通过
     let num1:number = null   //编译通过
    
    
    • 在typescript中,void表示空值,不返回任何类型
    
      let n:void
      let num:number = n  
      //编译不通过  因为 void不是所有类型的子类型,不像null 和 undefined
    
      function people():void{
         console.log('hello world')
      }
    
    
  • 2,任意值

    • 在typescript中,any表示任意值类型。 变量声明的时候,没有声明类型,默认为any
    
     let p:any = 'zhangsan'
    
     let n:any = 123
       n = '123'
    
     let a:any = []
    
     let b=true  //  默认为any
    
     //编译结果:
     var p = 'zhangsan'
     var n = 123
     n='123'
     var a =[]
     var b = true
    
    //像这样的,类型指定为any,可以是任意类型的。
    //如果,是一个普通类型,那么在赋值过程中改变类型是不允许的。
    
    
  • 3,类型推论

    • 如果在声明的时候,没有明确的指定类型,那么typescript会按照类型推论的规则推断出一个类型。
    let a = 'lisi'
    a = 20
    //编译报错,myts.ts:24:1 - error TS2322: Type 'number' is not assignable to type   'string'.
    
    

    类型推论,得到的是:

    let a:string = 'lisi'
    a = 20
     //所以,会编译报错
    
    

    但是,如果在定义的时候,没赋值:都会被推断any类型而完全不被类型检查

    let b;
    b='wangwu'
    b=20
    //可以看出,编译成功
    // 定义的时候,没有赋值,不管之后有没有赋值,都会被推断any类型而完全不被类型检查
    
    
  • 4,联合类型

    • 声明类型的时候,可以声明多个。用 | 分割
    let somethings:string | number
    somethings ='ABCD'
    somethings = 24
    
    //编译成功,结果为
    var somethings
    somethings= 'ABCD'
    somethings = 24
    
    

    但是,在赋值时,不属于该声明的类型:

     let somethings:string | number
     somethings ='ABCD'
     somethings = false
    
      //编译出错,但是编译结果(正确)为:
     var somethings
     somethings= 'ABCD'
     somethings = false
    
    

    这个时候,会编译报错:

    联合类型-1.jpg

    • 联合类型的方法:
    
      function people(attrs:string|number):number{
          return Math.ceil(attrs)
       }
       //报错
       
      
    

    这个时候,编译也会报错:因为Math.ceil不是string 和 number 的共同属性

    联合类型-2.jpg

    • 联合类型的属性:
    let p1:string|number
    p1 =  'zhangsan'
    console.log(p1.length)   // 8 
    
    p1 = 20
    console.log(p1.length)   //编译报错
    // 因为类型 "number"不存在属性 "length"
    
    

    这个时候,会编译报错:类型 "number"不存在属性 "length"

    联合类型-3.jpg

    所以,访问联合类型的属性或者方法时,要兼顾类型的共有属性,才不会出现编译报错

  • 5,接口

    • 接口是面向对象JavaScript程序员的工具箱中最有用的工具之一
    • 在JavaScript中模仿接口主要有三种方式:通过注释、属性检查和鸭式辩型法(可以自行查看网上资料),以上三种方式有效结合,就会产生类似接口的效果。
    • 此接口并非是,我们所说的前后端联调的接口。
    • 在typescript中,使用interface来定义对象的类型。

    下面举个简单的例子:

    
      interface Dog{
        name:string;
        color:string;
        age:number
     }
     let xiaohei:Dog = {
        name:'小黑',
        color:'black',
        age:2
     } 
    // 接口: 主要是是一种规范,一种约束。
    
    

    使用 interface定义了一个Dog的接口,定义一个变量xiaohei,它的类型是Dog。

    • interface 定义了属性或者方法,在约束变量的时候,需要满足interface定义接口里所有的属性。

      当少了一些属性:

       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()
      
      //编译报错,因为缺少interface定义接口中的eat方法
      
      //所以,需要将eat()注释放开,这时编译成功
      
      

      这个时候,编译报错:

      接口-1-少属性.jpg

      当多一些属性时:

      
        interface Dog{
           name:string;
          color:string;
       }
       let xiaohei:Dog = {
           name:'小黑',
          color:'black',
          age:2
      } 
      //也是不允许的。这时编译也会报错
      

      这个时候,编译报错:

      接口-2-多属性.jpg

    • 6,可选属性

    这个时候,我们想,可不可以不要完全匹配,定义接口的属性。

    答案是有的 ,用关键字: ?

      interface Dog1{
         name:string;
         color:string;
         age?:number;
      }
     let xiaohei:Dog1 = {
         name:'小黑',
         color:'black'
     } 
     
     //编译成功,未报错。可以看出,这时,用关键字?可以解决不完全匹配。即,可选属性
    
    

    接口-3-可选属性.jpg

    • 7,任意属性 我们希望,定义一个未知的属性。但是,目前我们不知道该属性名,请问支持吗?

    答案是有的,用关键字[unknownName] 。 注意:unknownName是代表未知属性名,可随便定义。

    interface Student{
        name:string;
        age:number;
       [unknownName:string]:any;
    }
    
    let zhangsan:Student = {
        name:'zs',
        age:10,
        classNo:20,
        school:'一中'
    }
    
    //编译成功
    
    

    接口-4-任意属性.jpg

    当我们,把 [unknownName:string]:string。这时候:

    
       interface Student{
          name:string;
          age:number;
          [unknownName:string]:string;
      }
    
     let zhangsan:Student = {
          name:'zs',
          age:10,
          classNo:20,
          school:'一中'
     }
    
    //编译报错   
    // 类型"number"的属性"age"不能赋给字符串索引类型"string"
    // 不能将类型"number"分配给类型"string"
    

    接口-5-任意属性2.jpg

    可以看出,任意属性一旦定义,那么确定属性和可选属性的类型都必选是它的类型的子集。

    于是,可以结合联合属性:

      interface Student{
          name:string;
          age?:number;
          [unknownName:string]:string|number
    }
    
    let zhangsan:Student = {
        name:'zs',
        classNo:20,
        school:'一中'
    }
    //编译成功
    
    • 8,只读属性 在javascirpt的表单中,我们可能不希望,别人修改表单的数据,于是我们可以设置,只读属性。

    在typescript 中,同样存在只读属性。

    
      interface Student{
         readonly name:string;
         age?:number;
         [unknownName:string]:string|number
      }
    
     let zhangsan:Student = {
        name:'zs',
        classNo:20,
        school:'一中'
     }
     zhangsan.name ='lisi'
    
     //编译报错
     // 无法分配到"name",因为它是只读属性
    

    接口-6-只读属性.jpg

    注意:【当Student的属性name设置了readonly时,在定义变量zhangsan的时候,name必须存在】

    • 9,数组的类型

      • 表示法一:( [类型+ 方括号] )
      
      let arr:number[] = [1,2,3,4,5]
      //编译成功
      
      let arr1:number[] =['1',2,3,4,5]
      //编译报错 : 不能将类型"string"分配给类型"number"
      //这是因为:我们定义了数组arr1的类型,是number,所以数组内的每一项必须是number
      
      arr.push('zs')
      //编译报错: 类型"string"的参数不能赋给类型"number"的参数
      //这是因为: 向arr数组中添加了字符串'zs',不满足 每一项必须是number
      
      

      数组的类型-1.jpg

      • 表示法二:(泛型)
        let a:Array<number> =[1,2,3,4,5]
       // 数组泛型  可以参考下节内容
      
      • 表示法三:(接口表示法)
        interface Student{
            [index:number]:string
        }
        let zs:Student =['name','age','classno']
      
        //编译成功
      
      • any在数组中应用
        let arr:any[] = ['zs',12,'一中']
      
    • 10,函数的类型 (1)在typescript中,函数也有两种表示方式: 匿名函数和函数声明法

    function a():string{
      return '张三'
     }
    
    var  f = function():number{
        return 123
    }
    

    (2)在typescript中定义方法传参

      function sum(x:number,y:number):number{
         return x+y
      }
    
      var  fsum2= function(x:number,y:number):number{
         return x+y
      }
    

    (3)在typescript中,方法的可选参数

     // es5里面方法的实参和形参可以不一样。但是ts中必须一样,如果不一样就需要配置可选参数
    
    function getInfo(name:string,age?:number):string{
       if(age){
            return `${name}---${age}`
       }else{
           return `${name}---年龄保密`
       }
    
    }
    
    console.log(getInfo('zhangsan',123))
    console.log(getInfo('lisi'))
    
    

    (4)在typescript中,方法的默认参数

       function sum(a:number,b:number=20):number{
          return a+b
       }
       alert(sum(10))    //30
       alert(sum(10,10))   // 20
    

    (5)在typescript中,剩余参数法

      // 三点运算符  接受形参传过来的值
    
     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))   // 10
    

    (6)在typescript中,函数重载

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

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

     //  es5 的重载
    
    functin sum(a){ console.log('0000')}
    function sum(a,b){consle.log('1111')}
    //注意:[下面的函数会覆盖上面的函数。]
    

    typescript 的重载

     function getInfo(name:string):string;
     function getInfo(age:number):string;
     function getInfo( str:any):any{
         if(typeof str==='string'){
            return '我叫'+ str
         }else{
           return '我的年龄'+ age
       }
    
     }
     alert(getInfo('张三'))   // 我叫张三     //正确写法
     alert(getInfo(20))   // 我的年龄 20    // 正确写法
     alert(getInfo(true))   //错误写法
    
    • 类型断言

    简单来说就是先做好一个假设,使得编译通过。类型断言更像是类型的选择,而不是类型转换。

    推荐以 as 方式,因为 jsx 这样的语法中只支持 as 方式。

    
     function func(val: string | number): number {
       if (val.length) {
           return val.length
       } else {
          return val.toString().length
       }
    }
    
    //编译报错
    

    类型断言-1.jpg

    因为 访问联合类型值的属性时,这个属性必须是所有可能类型的共有属性, 而length不是共有属性,val 的类型此时也没确定,所以编译不通过。

    当我们使用类型断言:

      function func(val:string|number):number{
         if((val as string).length){
            return (val as string).length
        }else{
            return val.toString().length
        }
      }
    // 编译成功
    // 把 val 断言为了 string类型,此时就可以访问 length 属性了
    

    类型断言纯粹是一个编译时语法,同时也是一种为编译器提供关于如何分析代码的方法。

    双重断言

    • 任何类型都可以被断言为 any
    • any 可以被断言为任何类型
       function handler(event:Event){
          const mouseEvent = event as MouseEvent
       }
    

    然而,下面的例子

    function handler(event:Event){
       const mouseEvent = event as HTMLElement
    }
    
    //Error:'Event' 和 'HTMLElement' 中的任何一个都不能赋值给另外一个
    

    如果继续使用那个类型,可以使用双重断言

    先断言炒年糕兼容所有类型的any,再断言成想使用的类型:

    function handler(event:Event){
        const element =(event as any) as HTMLElement   // ok
    }
    
    

    五,结束

    基础内容的这块笔记,暂且就这些。下一篇,开始是 typescript进阶内容

    经常看到身边的小伙伴,总是说,又出新技术了,又迭代了,又整活了,真的学不动了,干不动了... meybe 学习的方式不对,心态不对。

    多练,多读,多实践。慢慢进步,量变才能诱发质变。放好心态,加油吧!