TypeScript防脱发级入门——基本类型检查

780 阅读9分钟

嗨!大家好!我是法医,一只治疗系前端码猿🐒,与代码对话,倾听它们心底的呼声,期待着大家的点赞👍与关注➕。

1. 如何进行类型约束

类型约束其实很简单,只需要在变量函数参数函数返回值位置上加上:类型就可以了。

举个栗子 🌰: 变量

//我们定义变量的时候,肯定是知道这个变量是存放什么类型的数据

let name:string = "法医";

一旦给 name 赋值其它类型,立马会提示错误

image.png

举个栗子 🌰: 函数参数和返回值

//参数后面 :number表示传的参数必须是数字类型,而test函数后面的 :number表示返回值是数字类型
function test(a:number,b:number):number {
    return a + b;
}
test(1,2);//当调用test函数传值为数字表示可以正常运行,传其它类型则会报错

传入字符串就会报错

image.png

当我们写一个函数的时候,我们非常清楚函数的参数以及返回值是什么类型的,此时我们可以约束好类型,在之后的调用中我们可以放心的调用函数,因为只要写错了,立马会提示错误,不需要等到程序运行后再提示错误,这些在JS中是做不到的,但是在TS中很轻松可以做到,不仅如此,类型检查还带来很多好处,比方说:

举个栗子🌰:

JS中我们是没有办法确定下面代码中test(1,2)调用的就是一个函数,中途有可能test会被修改,然后调用函数就会报错

function test(a,b) {
    return a + b;
}
// 很多行代码
test = 123;
// 很多行代码
test(1,2);

image.png

但是在TS中这种情况绝对是不允许的 🙈

image.png

由于TS知道函数test和调用函数test是同一个东西,于是就出现一个神奇的效果——当需要给函数重新命名的时候,双击函数test并且按F2,函数名改了,调用函数名也跟着改了,之所以会达到这个效果,是因为TS有严格的类型检查系统,它知道调用函数的test用的就是test函数,它们两者之间是建立联系的

GIF 2021-9-7 15-34-20.gif

不仅如此,还有一种效果:当我们点击调用函数并且按F12,它会跳到定义的函数位置,

GIF 2021-9-7 15-43-12.gif

为了让我们少写点代码,使用TS进行约束的时候,TS在很多场景中可以完成类型推导

举个栗子🌰:

当我们把函数返回值约束去掉以后依然可以从提示中发现返回值是number,这是因为我们将参数约束为number,数字与数字相加依然是数字,所以最后函数也会返回number,赋值给变量result,TS还会智能地发现函数返回的结果是number,所以result类型也是number,因此我们只需要在参数位置加上类型约束就可以了,TS在每个地方都有类型检查,是不是很牛逼🐮

GIF 2021-9-8 12-58-32.gif

📢 那问题来了:我怎么知道这类型推导什么时候能推导成功,什么时候推导失败呢?

👉 解答:

有个小技巧,当我们看到变量或者函数的参数出现三个小点,这三个点就是在提醒:你给我当心点,我确实做不到了,表示当前没有推导出来到底是什么类型,可以用any类型来表示,这时就需要手动约束一下,

image.png

any:表示任意类型,对该类型,TS不进行类型检查

2. 基本类型

注意是首字母是小写

  • number:数字,

    let figure:number = 6;
    
  • string:字符串

    let user:string = "法医";
    
  • boolean:布尔值

    let fake:boolean = false;
    
  • array:数组

    :number[]这种写法其实是语法糖,真正的写法是下面第二种,这两种写法都可以约束数组的,看个人喜好,建议使用第一种,因为在react中尖括号表示组件,Array<number>可能会造成冲突

    let arr:number[] = [1,2,3];
    
    let arr:Array<number> = [1,2,3];
    
  • object:对象

    object约束不是很常用,因为object约束力不是很强,它只能约束一个对象,却不能约束对象里面的内容,但是有时会用到

    //传入一个对象,输出value值
    function getValues(obj:object) {
       let vals = Object.values(obj);
       console.log(vals); // 输出 法医  18   
    }
    getValues({
       name:"法医",
       age:18
    })
    
  • null和undefined

    nullundefined需要重点说一下,nullundefined是所有其他类型的子类型,它们可以赋值给其它类型,但是又会发生隐患,下面方法调用都会报错,由于约束了是stringnumber,但是值又是nullundefined,这种情况是我们不希望发生的。

    let str:string = null;
    let nums:number = undefined;
    
    //下面都会报错,由于约束了是string和number,但是值又是null和undefined
    str.toLocaleUpperCase();
    nums.toString();
    

    👉 解决方案:

    tsconfi.json配置文件中加上:"strictNullChecks": true之后可以获得更加严格的空类型检查,nullundefined就不能赋值给其它类型了,只能赋值给自身

    image.png

3. 其它常用类型

  • 联合类型:多种类型任选其一

    当一个变量既可以为字符串又可以为undefined的时候就可以使用联合类型,它可以配合使用类型保护进行判断

    📢 类型保护:当对某个变量进行类型判断之后,在判断的语句中便可以确定它的确切类型,tyoeof可以触发类型保护,但是它只能触发简单基本的类型保护,复杂类型是没有办法触发的

    let user:string | undefined;
    
    if(typeof user === "string"){
        //类型保护,当进入这个判断,TS一定会知道,此时user一定是字符串
    }
    
  • viod类型:通常用于约束函数返回值,表示该函数没有任何返回

    viodJs也是有的,表示运算一个表达式之后返回undefined,但在TS意思是不同的,通常用于约束函数返回值,表示该函数没有任何返回

    function user():void{
        console.log("法医");
    }
    

    当然不约束也是可以的,因为会类型推导出来

    image.png

  • never类型:通常用于约束函数返回值,表示该函数永远不可能结束

    function thorwError(msg:string) {
        throw new Error(msg)
    }
    

    这个函数的类型推导是有问题的,推导的类型是viod,因为它永远不会结束,类型应该是never而不是viod,所以需要手动更改

    GIF 2021-9-8 16-37-29.gif

    function thorwError(msg:string):never {
       throw new Error(msg)
    }
    

    由于是永远不会结束,所以,下面的log函数无法执行,无法访问代码

    image.png

    还有一种情况也是永远不会结束,需要手动约束

    GIF 2021-9-8 16-53-09.gif

  • 字面量类型:使用一个进行约束,而不是类型约束

    //表示从此以后,变量name只能是 “法医”,别的就会报错
    let name:"法医";
    

    GIF 2021-9-8 17-18-55.gif

    一般我们可以用字面量类型对性别或者对象中的属性进行约束:

    //对gender 变量进行约束,只能是男或女,其它不行
    let gender :"男" | "女";
    
    //对users对象中的name和age属性分别约束为字符串和数字,下次给users赋值的时候,只能包含name和age
    let users:{
        name:string
        age:number
    }
    
  • 元组类型(Tuple):用的不多,了解一下就可以了,表示固定长度的数组,并且数组中的每一项类型确定

    //定义了一个变量为tupleType的数组,这个数组只能有两项,并且第一个必须为字符串,第二个必须为数字
    let tupleType:[string,number];
    
    //第一项必须为字符串,第二项必须为数字,只能有两项,否则报错
    tupleType = ["法医",5];
    
  • any类型:any类型可以绕过类型检查,因此any类型可以赋值给任意类型,但肯定是有隐患的,因为它无法使用TS提供的保护机制,所以不建议随意的使用any类型,为了解决any带来的问题,TS3.0引入了unknown类型

    GIF 2021-9-8 19-19-54.gif

4. 类型别名

给已知的类型起个新的名字,防止重复书写一些代码

type Gender = "男" | "女";
type user = {
    name:string
    age:number
    gender:Gender
}

function getUser(g:Gender) {
    //...
}

5. 函数的相关约束

  • 函数重载

先看一个函数combine,功能是如果传递两个数字作为参数的时候相乘,传递两个字符串的时候相加,不相同都会报错。

function combine(a:number | string,b:number | string):number | string {
    if(typeof a === "number" && typeof b === "number"){
        return a * b;
    }
    else if(typeof a === "string" && typeof b === "string"){
        return a + b;
    }
    throw new Error("a和b必须是相同的类型")
}

函数本身没有什么问题,问题就发生在函数调用的过程中,当我们代码写多了以后,我们也许会失误传递不同的类型作为参数,更可怕的是如果参数是函数的返回结果,那就更蒙了,因此,在函数的调用过程中最好告诉调用函数,要么都是数字类型,要么都是字符串类型。

GIF 2021-9-8 20-57-48.gif

从逻辑上来说,都是数字的话返回的结果就是数字类型,都是字符串的话返回的结果就是字符串类型,然而result的类型是string | number,上图可以清晰看到,这种情况,后面就没有办法使用result变量了,因为明明知道都是数字返回的结果一定是数字类型,都是字符串返回的一定是字符串类型。意味着代码提示中不会出现所有数字拥有的方法或者所有字符串所拥有的方法,只会提示数字和字符串共同拥有的方法——toStringvalueOf如下图:

GIF 2021-9-8 21-06-21.gif

👉 解决方案:

加上下面两句代码,这两句代码相当于告诉 TS combine函数只能有两种情况,一种是两个数字返回数字,另一种是两个字符串返回字符串,这两句代码就叫函数重载

📢 函数重载:在函数实现之前,对函数调用的多种情况进行声明。

//加上这两句代码
/**
 * 得到a * b的结果
 * @param a 
 * @param b 
 */
function combine(a:number,b:number):number;
/**
 * 得到a + b的结果
 * @param a 
 * @param b 
 */
function combine(a:string,b:string):string;

function combine(a:number | string,b:number | string):number | string {
    if(typeof a === "number" && typeof b === "number"){
        return a * b;
    }
    else if(typeof a === "string" && typeof b === "string"){
        return a + b;
    }
    throw new Error("a和b必须是相同的类型")
}

let result = combine("b","n");

使用函数重载之后,当调用函数的时候只能传两个数字或者两个字符串,否则会报错,再来看看效果:

GIF 2021-9-8 21-23-10.gif

  • 可选参数

📢 可选参数:可以在某些参数名后面加上号,表示该参数可以不用传递。可选参数必须要在参数列表的末尾

image.png

当形参为三个,调用函数却传了两个,就会报错,TS是很严格的,不允许参数数量不匹配。假设第三个参数可以不传递,加个号表示是可选参数

image.png