TypeScript入门

1,247 阅读8分钟

前言

按照TS官网的介绍,ts是一门javaScript的超集。所谓超集意思就是,js有的东西,我ts有,js没有的,我ts还有,那到底ts提供给了我们哪些额外的东西了呢,先让我们看下js这门语言的简单描述,然后一切就明白了。

js的语言特点

JavaScript是一门动态弱类型语言。

熟悉js的同学知道,当我们声明了一个变量后,比如赋值了一个字符串,但是后续仍然可以将其再赋值成其它类型的数据,这个就是弱类型的表现。

 // 先声明一个字符串
 let a = 'string'
 // 后重新赋值成数字,这里是正常运行,不报错
 a = 123

相反那些,声明后不能修改变量值类型的语言,就是强类型语言。

动态性是指,只有在执行阶段才会确定所有变量的类型,我们知道,js是一门脚本语言,不需要事先编译,运行js代码时,js引擎会边编译边执行。

// 因为入参不确定,所以这个js函数只有在执行时,才能知道返回值是什么类型
 function add(x,y){
   return x + y
 }
 
 add(2,4) // return 6 返回number类型
 add('a','b') // return 'ab' 返回string类型

那么静态类型语言在编译阶段就会确定所有变量的类型。

js痛点

讲一下我在实际使用js的遇到的一些问题

  1. 项目庞大、代码量增多后,在后期维护阶段,对于一个变量的类型可能会不十分清楚。常常发生,调用一个变量的方法时,结果该变量值为null,然后报错的情况。
  2. 联调接口时,后端会返回一个层级较深,属性较多的对象,而我在使用它写业务代码时,只能边看着接口文档,才能知道里面都有什么东西,效率上有所影响。
  3. 接口的返回数据,没有类型约束,使用时常常发生TypeError。

TS提供的功能

  1. 我认为ts就是将js变成了一门静态强类型的语言,它提供了类型约束,它可以帮助我们重塑类型思维,减少代码的出错。
  2. 借助vscode的代码提示功能,能够提升开发的效率。

TS的使用

因为编写ts并执行,需要新建npm工程项目,以及构建工具的打包与编译,但这里并不介绍相关的内容,仅介绍下ts的基础用法。

基础类型

对于js原有的那些数据类型,在用ts声明时,只需在变量后加一个冒号以及类型名即可。

 // 字符串类型
 let name:string = 'typescript';
 
 // 数字类型
 let age: number = 18;
 
 // 此时如果给age赋值其它类型,就会报错
 age = '18' // Type 'string' is not assignable to type 'number'.
 
 // 布尔类型
 let isFlag:boolean = false
 
 // Null类型
 let isNull:null = null
 
 // undefiend类型
 let isUnde:undefined = undefined
 
 // symbol类型
 let id:symbol = Symbol(1)
 
 // 对象类型
 let peolpe:object = {
    id
    name,
    age
 }
 
 // 数组类型,定义一个数组,且组内成员的类型为数字
 let list:number[] = [1,2,3]
 

在实际使用中,其实单独的ts的null、undefined不是很常用,因为声明一个null或者undefined的变量没有意义,这种数据类型的声明也只是在js中使用,比如程序初始时,先声明一个null的变量,后续的代码逻辑中再改成其它数据类型。

除了这些数据类型,ts还新增了元祖、枚举类型。

其中枚举类型确实是个非常有用的类型,它可以为一个值赋予一个有意义的描述。

比如某些字段,有多个值,每个值有不同的含义,但如果我们直接使用这些值,来区分并写代码逻辑的话,后续维护时,会比较麻烦,因为你在编写代码前需要先搞明白这些值的含义。

js代码

  // 例如这个字段,它有三个值 1,2,3,分别代表学生、家长、老师
  let userType 
  
  switch(userType){
    case 1:
     // 学生用户的代码逻辑
    case 2:
     // 家长用户的代码逻辑
    case 3:
     // 老师用户的代码逻辑
  }

就这上述这段代码,如果没有注释的话,过一段时间看,可能你就不知道这些数值代表的是啥意思了,但用ts的枚举的话,就很好的解决问题了。

ts代码重写

 enum UserType {
   student = 1,
   parents = 2,
   teacher = 3
 }
 
let userType:UserType
  
  switch(userType){
    case UserType.student:
     // 学生用户的代码逻辑
    case UserType.parents:
     // 家长用户的代码逻辑
    case UserType.teacher:
     // 老师用户的代码逻辑
  }

这样是不是就一目了然了,增加了代码的可读性,后期维护也很容易。

接口

接口是前端之前没有过的一个概念,在基础类型中,我们只是约束了一个变量的类型,但是,在实际的开发中,我们会组织成一些具有结构的数据,而接口的作用就是对这些有结构的值进行类型约束,它也被叫做鸭式辨型法,意思就是如果一个东西,长的像鸭子,叫声像鸭子,那它就是鸭子。

接口可以约束对象、函数、和类的结构。

举个例子,就清楚了

对象接口

 // 定义一个约束对象的接口,
 interface People {
   age:number;
   name:string;
 }
 
 // 用接口对变量约束
 
 let child:People = {
   age: 18,
   name: 'xiaoming'
 }

这样就通过接口,完成了对child的类型检查,但是接口的约束是非常严格的,如果数据结构中多一个属性,或者少一个属性,ts都会提示报错,像这样。

 let child:People = {
   age: 18,
   name: 'xiaoming',
   // 增加了一个接口中未定义的变量属性
   tel: 177734234234
 }
 

不过接口可以添加一个可选属性

  interface People {
   age:number;
   name:string;
   // 不必须
   tel?: number;
 }

这样tel变量就是非必须的,声明时可以省略,类型检查不会报错。

函数接口

 interface Add {
   // 直接写函数类型
   (x:number,y:number):number
  }
  
 let add:Add = (a,b) => a + b

在实际编写函数时,函数里的参数名无需和接口里定义名称一样,ts会根据位置进行匹配。

索引类型接口

索引类型就是接口可以描述那些通过索引得到的类型,比如对象中访问属性,people[name], numList[0],可索引类型具有一个 索引签名,它描述了对象索引的类型,还有相应的索引返回值类型。 让我们看一个例子:

ts仅支持两种索引签名:字符串和数字

 interface NumList {
   // 数字索引,中括号内的类型约束属性名,后面的约束属性值
   [index:number] :string
 }
 
 let array:NumList = ['a','b']
 
 interface StringList {
   // 字符串索引
   [x:string] : string
 }
 
 let obj:StringList = {a:'a'}

泛型

泛型也是一个前端从没遇见过的概念,上面谈到,我们在声明一些变量的时候需要给这个变量定义好它的类型。

但是有时候,我们可能希望说,实际使用时,这个变量能够有不同的类型,这就需要泛型了。

就像是函数的入参一样,泛型可以理解为变量类型的参数,在实际使用时去传入。

比如定义一个函数

  function createValue(val: number):number {
     return val + val
  }

如果我们希望这个函数可以进行字符串拼接,而不是只能传数值时,则可以用泛型

 function createValue<T>(val: T) : T{
    return val + val
   }
  
 createValue<string>('234')
 
 createValue<number>(234)

使用一个尖括号,来作为类型的入参位置,实际调用时,再传入具体的数据类型。当然也可以不用想这样传入数据类型,因为ts的编译器会根据传入的值,来进行类型推论。

对TS的一点看法

  1. 首先我不太想把它当做是一门语言,因为它与js的关系,就像是sass、less与css的关系,虽然提供了一些额外的功能,但最终还是要编译成js,因为没有js引擎可以直接运行ts代码的。
  2. 最开始使用的时候,我甚至觉得它限制了原有js类型的灵活性,比如js中的各种显示和隐形的类型转换,但后续确实啪啪打脸。当我们的代码量庞大后,维护成本增加,有时候为了确定函数的一个入参变量的类型,都得找好久,但使用ts就没有这个问题。
  3. 当我们提供给其他人使用自己的函数时,可以约束入参的类型,减少问题产生。

ps: 如有错误或补充,欢迎评论交流