TypeScript介绍和基本数据类型

127 阅读9分钟

1.TypeScript简介

TS:是TypeScript的简称,是一种由微软开发的自由和开源的编程语言。

1.1 TS和JS的关系

对比于JS,TS是JS的超集,简单的说就是在 JavaScript 的基础上加入了类型系统,让每个参数都有明确的意义,从而带来了更加智能的提示。

相对于JS而言,TS属于强类型语言,所以对于项目而言,会使代码更加规范,从而解决了大型项目代码的复杂性,其次,浏览器是不识别TS的,所以在编译的时候,TS文件会先编译为JS文件。

1.2 安装TS

执行命令:

npm install -g typescript 
 //或
yarn global add typescript

1.3 查看TS版本

tsc -v

image.png

1.4 TS编译成JS

tsc test.ts
# test.ts => test.js

1.gif

1.5 在线编译TS

可以使用线上的编辑器:TypeScript Playground,像这样

image.png

并且你还可以看看ts转化成对应ES5ES6之后的代码

2. TS的基本数据类型

将TS的数据类型简单的进行下归类:

  • 基本类型:stringnumberbooleansymbolbigintnullundefined
  • 引用类型:arrayTuple(元组)、 object(包含Object{})、function
  • 特殊类型:anyunknowvoidneverEnum(枚举)
  • 其他类型:类型推理字面量类型交叉类型

注:还有typeinterface

2.1 TS基本数据类型

    //字符串
    let str: string = "Domesy"
    
    // 数字
    let num: number = 7
    
    //布尔
    let bool: boolean = true
    
    //symbol
    let sym: symbol = Symbol();
     
    //bigint
    let big: bigint = 10n
        
    //null
    let nu: null = null
    
    //undefined
    let un: undefined = undefined

image.png 需要注意:

  • nullundefined 两个类型一旦赋值上,就不能在赋值给任何其他类型
  • symbol是独一无二的,假设再定义一个 sym1,那么sym === sym1 为 false

2.2 引用类型

2.2.1 Array

两种方式:

  • 类型名称 + []
  • Array<数据类型>
    let arr1: number[] = [1, 2, 3]
    
    let arr2: Array<number> = [1, 2, 3]
    
    let arr2: Array<number> = [1, 2, '3'] // error
    
     //要想是数字类型或字符串类型,需要使用 |
    let arr3: Array<number | string> = [1, 2, '3'] //ok

image.png

image.png

2.2.2 Tuple(元组)

Tuple 可以说是 Array 的一种特殊情况,针对上面的 arr3,我们看他的类型可以是string也可以是number,但对每个元素没有作出具体的限制。

那么 Tuple 的作用就是限制元素的类型并且限制个数的数组,同时 Tuple这个概念值存在于TS,在JS上是不存在的

这里存在一个问题:在TS中,是允许对 Tuple 扩增的(也就是允许使用 push方法),但在访问上不允许

    let t: [number, string] = [1, '2'] // ok
    let t1: [number, string] = [1, 3] // error
    let t2: [number, string] = [1] // error
    let t3: [number, string] = [1, '1', true] // error


    let t5: [number, string] = [1, '2'] // ok
    t.push(2)
    console.log(t) // [1, '2', 2]

    let a =  t[0] // ok
    let b = t[1] // ok
    let c = t[2] // error

image.png

2.2.3 object

  • object 非原始类型,在定义上直接使用 object 是可以的,但你要更改对象的属性就会报错,原因是并没有使对象的内部具体的属性做限制,所以需要使用 {} 来定义内部类型
    let obj1: object = { a: 1, b: 2}
    obj1.a = 3 // error

    let obj2: { a: number, b: number } = {a: 1, b: 2}
    obj2.a = 3 // ok

image.png

  • Object(大写的O),代表所有的原始类型或非原始类型都可以进行赋值,除了null和`undefined
    let obj: Object;
    obj = 1; // ok
    obj = "a"; // ok
    obj = true; // ok
    obj = {}; // ok
    obj = Symbol() //ok
    obj = 10n //ok
    obj = null; // error
    obj = undefined; // error

2.2.4 function

2.2.4.1 定义函数

  • 有两种方式,一种为 function, 另一种为箭头函数
  • 在书写的时候,也可以写入返回值的类型,如果写入,则必须要有对应类型的返回值,但通常情况下省略,因为TS的类型推断功能够正确推断出返回值类型
    function setName1(name: string) { //ok
      console.log("hello", name);
    }
    setName1("Domesy"); // "hello",  "Domesy"

    function setName2(name: string):string { //error
      console.log("hello", name);
    }
    setName2("Domesy");

    function setName3(name: string):string { //error
      console.log("hello", name);
      return 1
    }
    setName3("Domesy");

    function setName4(name: string): string { //ok
      console.log("hello", name);
      return name
    }
    setName4("Domesy"); // "hello",  "Domesy"

    //箭头函数与上述同理
    const setName5 = (name:string) => console.log("hello", name);
    setName5("Domesy") // "hello",  "Domesy

2.2.4.2 参数类型

  • 可选参数: 如果函数要配置可有可无的参数时,可以通过 ? 实现,但可选参数一定要在最后面
  • 默认参数:函数内可以自己设定其默认参数,用 = 实现
  • 剩余参数:仍可以使用扩展运算符 ...
    // 可选参数
    const setInfo1 = (name: string, age?: number) => console.log(name, age)
    setInfo1('Domesy') //"Domesy",  undefined
    setInfo1('Domesy', 7) //"Domesy",  7

    // 默认参数
    const setInfo2 = (name: string, age: number = 11) => console.log(name, age)
    setInfo2('Domesy') //"Domesy",  11
    setInfo2('Domesy', 7) //"Domesy",  7

    // 剩余参数
    const allCount = (...numbers: number[]) => console.log(`数字总和为:${numbers.reduce((val, item) => (val += item), 0)}`)
    allCount(1, 2, 3) //"数字总和为:6"

image.png

2.2.4.3 函数重载

函数重载:是使用相同名称和不同参数数量或类型创建多个方法的一种能力。 在 TypeScript 中,表现为给同一个函数提供多个函数类型定义。 简单的说:可以在同一个函数下定义多种类型值,最后汇总到一块

    let obj: any = {};
    function setInfo(val: string): void;
    function setInfo(val: number): void;
    function setInfo(val: boolean): void;
    function setInfo(val: string | number | boolean): void {
      if (typeof val === "string") {
        obj.name = val;
      } else {
        obj.age = val;
      }
    }
    setInfo("Domesy");
    setInfo(7);
    setInfo(true);
    console.log(obj); // { name: 'Domesy', age: 7 }

image.png

2.3 特殊类型

2.3.1 any

在 TS 中,任何类型都可以归于 any 类型,所以any类型也就成了所有类型的顶级类型,同时,如果不指定变量的类型,则默认为any类型, 当然不推荐使用该类型,因为这样丧失了TS的作用

    let d:any; //等价于 let d 
    d = '1';
    d = 2;
    d = true;
    d = [1, 2, 3];
    d = {}

image.png

2.3.2 unknow

any一样,都可以作为所有类型的顶级类型,但 unknow更加严格,那么可以说除了any 之下的第二大类型,接下来对比下any,主要严格于一下两点:

  • unknow会对值进行检测,而类型any不会做检测操作,说白了,any类型可以赋值给任何类型,但unknow只能赋值给unknow类型和any类型
  • unknow不允许定义的值有任何操作(如 方法,new等),但any可以
    let u:unknown;
    let a: any;

    u = '1'; //ok
    u = 2; //ok
    u = true; //ok
    u = [1, 2, 3]; //ok
    u = {}; //ok

    let value:any = u //ok
    let value1:any = a //ok
    let value2:unknown = u //ok
    let value3:unknown = a //ok
    let value4:string = u //error
    let value5:string = a //ok
    let value6:number = u //error
    let value7:number = a //ok
    let value8:boolean = u //error
    let value9:boolean = a //ok

    u.set() // error
    a.set() //ok
    u() // error
    a() //ok
    new u() // error
    new a() //ok

image.png

2.3.3 void

当一个函数,没有返回值时,TS会默认他的返回值为 void 类型

    const setInfo = ():void => {} // 等价于 const setInfo = () => {}

    const setInfo1 = ():void => { return '1' }  // error
    const setInfo2 = ():void => { return 2 } // error
    const setInfo3 = ():void => { return true } // error
    const setInfo4 = ():void => { return  } // ok
    const setInfo5 = ():void => { return undefined } //ok 

2.3.4 never

表示一个函数永远不存在返回值,TS会认为类型为 never,那么与 void 相比, never应该是 void子集, 因为 void实际上的返回值为 undefined,而 neverundefined也不行

符合never的情况有:当抛出异常的情况和无限死循环

    let error = ():never => { // 等价约 let error = () => {}
            throw new Error("error");
    };

    let error1 = ():never => {
        while(true){}
    }

2.3.5 Enum(枚举)

可以定义一些带名字的常量,这样可以清晰表达意图创建一组有区别的用例

注意:

  • 枚举的类型只能是 stringnumber
  • 定义的名称不能为关键字

同时我们可以看看翻译为ES5是何样子

2.3.6 数字枚举

  • 枚组的类型默认为数字类型,默认从0开始以此累加,如果有设置默认值,则只会对下面的值产生影响
  • 同时支持反向映射(及从成员值到成员名的映射),但智能映射无默认值的情况,并且只能是默认值的前面

image.png

2.3.7 字符串枚举

字符串枚举要注意的是必须要有默认值,不支持反向映射

image.png

2.3.8 常量枚举

除了数字类型字符串类型之外,还有一种特殊的类型,那就是常量枚举,也就是通过const去定义enum,但这种类型不会编译成任何 JS,只会编译对应的值

image.png

2.3.9 异构枚举

包含了 数字类型字符串类型 的混合,反向映射一样的道理

image.png

2.4 类型推论

我们在学完这些基础类型,我们是不是每个类型都要去写字段是什么类型呢?其实不是,在TS中如果不设置类型,并且不进行赋值时,将会推论为any类型,如果进行赋值就会推断为默认值类型

    let a; // 推断为any
    let str = '小杜杜'; // 推断为string
    let num = 13; // 推断为number
    let flag = false; // 推断为boolean

    str = true // error Type 'boolean' is not assignable to type 'string'.(2322)
    num = 'Domesy' // error
    flag = 7 // error

2.5 字面量类型

字面量类型:在TS中,我们可以指定参数的类型是什么,目前支持字符串数字布尔三种类型。比如说定义了 str 的类型是 '小杜杜' 那么str的值只能是小杜杜

    let str:'小杜杜' 
    let num: 1 | 2 | 3 = 1
    let flag:true

    str = '小杜杜' //ok
    str = 'Donmesy' // error

    num = 2 //ok
    num = 7 // error

    flag = true // ok
    flag = false // error

2.6 交叉类型(&)

交叉类型:将多个类型合并为一个类型,使用&符号连接,如:

    type AProps = { a: string }
    type BProps = { b: number }

    type allProps = AProps & BProps

    const Info: allProps = {
        a: '小杜杜',
        b: 7
    }

2.6.1 同名(基础数据类型)属性合并

可以看到交叉类型是结合两个属性的属性值,那么要是两个属性都有相同的属性值,那么此时总的类型会怎么样,先看看下面的案列:

    type AProps = { a: string, c: number }
    type BProps = { b: number, c: string }

    type allProps = AProps & BProps

    const Info: allProps = {
        a: '小杜杜',
        b: 7,
        c:  1, // error (property) c: never
        c:  'Domesy', // error (property) c: never
    }

image.png 如果是相同的类型,合并后的类型也是此类型,那如果是不同的类型会如何:

我们在ApropsBProps中同时加入c属性,并且c属性的类型不同,一个是number类型,另一个是string类型

现在结合为 allProps 后呢? 是不是c属性numberstring 类型都可以,还是其中的一种?

然而在实际中, c 传入数字类型字符串类型都不行,我么看到报错,现实的是 c的类型是 never

这是因为对应 c属性而言是 string & number,然而这种属性明显是不存在的,所以c的属性是never

2.6.2 同名(非基础数据类型)属性合并

    interface A { a: number }
    interface B { b: string }

    interface C {
        x: A
    }
    interface D {
        x: B
    }
    type allProps = C & D

    const Info: allProps = {
      x: {
        a: 7,
        b: '小杜杜'
      }
    }

    console.log(Info) // { x: { "a": 7, "b": "小杜杜" }}

image.png 我们来看看案例,对于混入多个类型时,若存在相同的成员,且成员类型为非基本数据类型,那么是可以成功合。

如果 接口A 中的 也是 b,类型为number,就会跟同名基础属性合并一样