14

为什么要用 typescript

typescript引入了类型,避免了诸如函数调用传错参数的低级错误:

clipboard.png

clipboard.png

提供了代码提示:
clipboard.png

如何使用

npm install typescript -g

这里需要说明一下的是,typescript是一个泛指,包括语言特性和语言相关的生态功能。要不然看官方的更新文档,就会有困惑,有很多明显不属于typescript语言的更新,比如重命名的时候对应的函数或文件跟着自动变了这种功能,很明显不属于语言的特性,应该是编辑器或者IDE负责的东西,咋就放到语言的更新日志里了呢?

从一个例子说起

来,我们看个例子:

function add(a:number, b:number):number {
    return a+b
}

上面的代码,我们声明了一个add函数,接收两个参数a和b,告诉编辑器和调用add函数的人,“这俩参数都是number类型,别传错参数啊,对了,我这个函数的返回值也是一个number类型,别指望我给你返回其他东西”。

我们可以看出,和JS最大的区别就是,增加了类型。

类型可以用在哪里

我们上面的例子里,类型用来标注函数的参数的类型、函数的返回值类型。此外也可以用来表示一个变量的类型。
比如:

var a:number=1

上面的代码的意思是,声明里一个变量a,这个变量a的类型是number类型。由于typescript的类型推断功能,我们上面的代码也可以写成这样:

var a=1

ts编译器看到后面的数字1之后,会智能的推断的出a是一个number类型,是不是发现ts还是很聪明的嘛

image.png

都有哪些类型

常用类型

除了我们上面说的number类型,还有这几个常用类型:string boolean undefined null 。
当然了复杂类型也是有的,

var list:string[]=[] 

表示的是变量list是一个数组,这个数组里面每个元素都是string。

对象

function f1(a: { name: string; age: number }) {

}

上面的代码,表示参数a是一个对象,这个对象上有name和age两个属性,并且这两个属性的类型分别是string和number,那么当我们调用这个函数的时候,就要出符合条件的参数,不符合的话,ts就提示代码写的有问题了。
image.png

我们也可以用interface来声明一个对象结构,

interface af {
    name: string
    age: number
}


function f1(a:af) {

}

和Java的interface有点像但不太一样,我们把它理解成用来声明一个结构,这个结构长成这个样子:有属性xxx,对应的值是string类型,有属性yyy,对应的值的类型是number……

有个注意的点是,当我们传入的参数多了一些的属性的话,ts也会提示报错:
image.png
那么要解决这种情况,一种方式是改造我们的interface成这样子:

interface af {
    name: string
    age: number
    [attr:string]:any
}

多出来的[attr:string]:any意思是说我这个af上,还可能有一些其他的属性,对应的属性值的类型是any。
同样的,当我们往a上面赋值一些新的属性的时候,也会提示,
image.png
我们也要加[attr:string]:any

另外一种方法参看文章后面的类型断言。

函数类型

image.png
现在,我们这里有个参数callback,它是一个函数类型,那怎么声明呢:
image.png
其中void表示这个函数没有返回值。那通常,我们的回调函数会需要一些参数来接受数据:
image.png

当然我们也可以把这个callback的类型给提取出来,用interface声明一个函数类型(是的,你没看错……就是刚才用来声明对象的interface,目前你知道了它有两个作用了)

interface ccc{
    (data:string):void
}

function getInfo(id:number,callback:ccc){

}

注意写法有点稍微不一样,一个是=>void 一个是:void

字面量类型

image.png

image.png
上面,a3和a4就只能是固定的字面量"name"或者1了。

还有个any类型,顾名思义,表示任意类型,是其他类型的子类型。
image.png
你看,本来f2是要传一个number进来的,我们传了一个any类型进去也不报错,一般是,当我们不指明变量(常量、参数、返回值)类型,ts又推断不出来的时候,ts会推断成any类型。(这个需要到tsconfig.json里面配置)

类型之间运算

|表示或的意思:
image.png
比如这里,我们告诉编译器,u1这个变量可能是1也可能是2,也可能是3。
再比如:

var u2:"aaaa"|1|{a:string}

可能是三种完全不同的类型。
&表示合并两个类型:
image.png
这样,u1就有a和b加一块的属性了。

声明一个类型

使用type来声明一个类型,比如说,上面的一长串"aaaa"|1|{a:string}我们再很多地方都要用到,我们可以提取出来,起个名字供大家调用:

type uu="aaaa"|1|{a:string}

var u3:uu
...
var u4:uu

interface a {
  aaa: string
}

interface fc {
  (data: string): void
}

type uu2 = a & fc

类型断言

啥是类型断言呢,顾名思义,就是对类型的判断。比如我们上面举的一个例子:当传进来的参数不符合要求时,
image.png

使用as语句,告诉编译器/编辑器,“你就把我传进来的参数当成符合af接口的数据吧”,相当于我人肉判断了,我传的参数类型我知道符合条件。
再比如:声明变量的时候:
image.png

非空断言

有一种情况,如下,当我们在tsconfig.json里面开启了严格的null检查的时候,
image.png
这时候,当然我们 使用as string就可以搞定,其实还有个小技巧,使用!告诉ts这个变量不是null也不是undefined
image.png

类型保护

说白了就是当我们使用一个变量的时候,当访问到可能不存在的属性或方法的时候,强制要求做类型判断:

使用typeof

比如这个例子:
image.png
参数a可能是number或者string,默认情况下,只能调用number和string都有的方法。当调用number独有的方法比如说toFixed的时候就报错了。这时候你就应该判断一下,如果是number类型的再调用toFixed
image.png
上面的例子,你会发现,ts还是挺智能的,自动推断出else的情况就是string类型。

使用 instanceof

image.png
instanceof 差不多,主要用来判断是不是一个类的对象。

使用 in

你可能会机智的想到了,那假如类型是interface呢,js里面可没有这东西
image.png

ts也做了非常聪明的推断,就不详细说了。

泛型

有时候,我们的几个类型非常相似,只有一两个参数不一样,比如常在CMS开发中定义接口返回值类型的情况,如下面的UserResponse和RoleResponse

interface User{
    name: string
    age:number
}

interface Role{
    name: string
    id:string
}

interface UserResponse{
    ret: number
    message:string
    data:User
}

interface RoleResponse{
    ret: number
    message:string
    data:Role
}

这时候我们就在想能不能把公共的部分提前出来作为一个通用的类型,然后这个通用的类型接收不同的参数并生成新的类型?当然是可以的:

interface CommonResponse<T>{
    ret: number
    message: string
    data: T
}
type UserResponse = CommonResponse<User>
type RoleResponse=CommonResponse<Role>

你会发现泛型有点类似于函数参数。

泛型默认值

和函数参数一样,我们也可以给泛型设置个默认值,当不传的时候就使用默认值。

interface T1<T = any>{
    aaa: string
    t:T
}

多个泛型

也支持多个泛型,

interface T1<T ,K> {
    aaa: string
    t: T
    k:K
}

泛型约束 extends

有时候,我们想要约束一下传进来的泛型的类型:
使用extends来约束
image.png

泛型除了可以用在interface上,还可以用在type、class、function上:

type t1<T> = {
    aaa: T
}

type t2<T> = string | T

class C<T> {
    aaa: T
    constructor() {}
}


function get<T>(a:T) :T{
    return a
}

var g = get<string>("")

这里说一个特别常用的例子:Promise,
image.png
不加泛型的时候,ts不知道await get()的返回值是什么类型,加了泛型之后就知道了,并且能智能提示了:
image.png

tsconfig.json

本质上,我们写的ts代码最终会编译js代码,比如删除掉ts代码里面的类型接口等。那么这个编译行为有许多参数可以控制,最常用的方法是在项目根目录创建一个tsconfig.json的文件,ts编译代码的时候会根据里面的配置进行编译。

通过命令行执行tsc将ts代码编译成js代码。tsc -w监听文件变化自动编译代码。

tsc -init可以生成tsconfig.json文件。


Midqiu
1.5k 声望48 粉丝