TypeScript:基础知识了解

161 阅读8分钟

DAY1

为何TS?

  1. JS是一门优秀的语言:web端-js,移动端-ReactNative/Weex/Uniapp,小程序端-js,桌面端-Electron,服务器端-js

  2. JS痛点:在类型检测上没有进展

    1. 类型检测:错误出现越早越好

      1. 静态检测:写代码的时候发现错误-IDE
      2. 代码编译期间:类型检测--JS难以做到
      3. 尽量避免在代码运行期间、开发阶段、测试阶段发现错误-越早发现越好
    2. JS没有对传入参数进行任何限制,只有等到运行期间才发现错误,影响后续代码继续执行

    3. JS未考虑类型约束,造成前端开发人员关于类型思维的缺失

    4. 对于大型项目,宽松的类型约束会带来很多的安全隐患,多人员开发时,没有良好的类型约定

什么是TS?

定义

静态类型检查器:TypeScript 在执行之前,基于 值的类型 检查程序是否有错误。它是 静态类型检查器

JavaScript 的类型化超集: TS对JS拥有的特性全部支持,且支持ES标准;语言层面上,不仅增加了类型约束,包括了一些语法的扩展;TS最终会被编译位JS代码(tsc/babel)

运行环境

方式一:

通过tsc编译TS到JS代码--webpack,配置本地的TS编译环境和开启一个本地服务,可直接运行到浏览器上

npm install typescript -g

方式二:在浏览器或者Node环境下运行JS代码--ts-node库,为TS的运行提供执行环境

npm install ts-node -g

npm install tslib @types/node -g

运行:ts-node 文件名.ts

TS变量声明

变量声明:指定标识符的类型

完整声明格式:var/let/const 标识符: 数据类型 = 赋值 注意:数据类型是小写

例如:let message: string = 'hello' (string为TS中定义的字符串类型,String为js中字符串的包装类)

声明类型后TS就会进行类型检测,声明的类型称为类型注解

声明变量的关键字

同es6:var/let/const定义

变量的类型推断

一个变量第一次赋值时,会根据后面的复制内容的类型,来推断变量的类型

  • TS数据类型

    JavaScript 中已经有一些基本类型可用:booleanbigintnullnumberstringsymbolundefined,它们都可以在接口中使用。TypeScript 将此列表扩展为更多的内容,例如 any (允许任何类型)、[unknown](https://www.typescriptlang.org/play#example/unknown-and-never) (确保使用此类型的人声明类型是什么)、 [never](https://www.typescriptlang.org/play#example/unknown-and-never) (这种类型不可能发生)和 void (返回 undefined 或没有返回值的函数)。

    构建类型有两种语法: 接口和类型。 常用 interface。当需要特定功能时使用 type

image.png

支持JS数据类型

    // 数字 布尔 字符串类型 ---这些用法和JS一样
    let num: number = 123.3
    let flag: boolean = true
    const name = 'lgl'

    // 数组类型  1.数组中存放的类型[] 2.Array<数组中存放的类型> (泛型写法)
    let names: string[] = ["aaa", "bbb", "ccc"]
    let nums: Array<number> = [111, 222, 333.1];

    // 对象类型 
    //1.直接object--info.name,info.age等报错,不能获取数据和设置数据
    const info: object = {
      name: "xdh",
      age: 18,
    };
    //2.使用interface type等,或者写全(若补充不全也报错)
    let info:{
      name:string,
      age:number
    } = {
      name: "xdh",
      age: 18,
    }


    // null undefined类型
    const n1: null = null;
    const n2: undefined = undefined;

    // Symbol类型:Symbol函数返回的是不同值
    const title1:symbol = Symbol("title")
    const title2:symbol = Symbol('title')
    const info = {
      [title1]: "程序员",
      [title2]: "老师"
    }

    //函数类型 定义参数类型,定义返回类型

    // 1.定义对象类型
    type LyricType = {
      time: number
      text: string
    }
    // 2.歌词解析工具 传入参数lyric为string,返回一个数组,每一个项是对象
    function parseLyric(lyric: string): LyricType[] {
      const lyrics: LyricType[] = []
      lyrics.push({ time: 1111, text: "天空想要下雨" })
      return lyrics
    }

    const lyricInfos = parseLyric("fdafdafdafa")
    for (const item of lyricInfos) {
      console.log(item.time, item.text)
    }

    //可选类型:对象类型也可以指定哪些属性可选,在属性后面添加?
    // 可以对象类型和函数类型结合使用
    type PointType = {
      x: number
      y: number
      z?: number
    }
    function printCoordinate(point: PointType) {
      console.log("x坐标:", point.x)
      console.log("y坐标:", point.y)
    }

TS特有特性

  • any类型

    在 TypeScript 中,any 类型可以被用来表示任何类型,包括原始类型、对象类型以及函数类型。

    使用 any 类型时,编译器将不会对该类型进行任何类型检查,允许在编译时和运行时对其进行任何操作,因此被称为“逃逸舱”类型。

    应该在必要的时候才使用 any 类型,尽可能使用更具体的类型来表示代码中的数据类型。在使用 any 类型时,可以通过类型断言等方式尽可能减少其使用范围,并在运行时进行必要的类型检查以保证代码的健壮性。

        // any类型就表示不限制标识符的任意类型, 并且可以在该标识符上面进行任意的操作(在TypeScript中回到JavaScript中)
        let id: any = "aaaa"
        id = "bbbb"
        id = 123
        console.log(id.length)
        id = { name: "why", level: 99 }
        // 定义数组
        const infos: any[] = ["abc", 123, {}, []]
    
  • unknown类型

    unknown类型是TS中比较特殊的一种类型,用于描述类型不确定的变量

    any类型类似,但unknown类型的值上做任何事情都不合法

    let foo: unknown = "aaa"
    foo = 123
    
    // unknown类型默认情况下在上面进行任意的操作都是非法的
    // 要求必须进行类型的校验(缩小), 才能根据缩小之后的类型, 进行对应的操作
    if (typeof foo === "string") { // 类型缩小
      console.log(foo.length, foo.split(" "))
    }
    export {}
    
  • void类型

    void通常用来指定一个函数是没有返回值的,那么它的返回值就是void类型

    基于上下文的类型推导推导出返回类型为void的时候,并不会强制函数一定不能返回内容

    // 1.在TS中如果一个函数没有任何的返回值, 那么返回值的类型就是void类型(可以显式指定,也可以不写)
    // 2.如果返回值是void类型, 那么我们也可以返回undefined(TS编译器允许这样做而已)
    function sum(num1: number, num2: number): void {
      console.log(num1 + num2)
    
      // return 123 错误的做法
    }
    
    
    // 应用场景: 用来指定函数类型的返回值是void
    type LyricInfoType = { time: number, text: string }
    // parseLyric函数的数据类型: (lyric: string) => LyricInfoType[]
    function parseLyric(lyric: string): LyricInfoType[] {
      const lyricInfos: LyricInfoType[] = []
      // 解析
      return lyricInfos
    }
    
    // parseLyric => 函数/对象
    //foo的类型是FooType->函数类型,没有返回值
    type FooType = () => void
    const foo: FooType = () => {}
    
    
    // 举个例子:(涉及函数的类型问题, 后续还会详细讲解)
    // 1.定义要求传入的函数的类型
    type ExecFnType = (...args: any[]) => void
    
    // 2.定义一个函数, 并且接收的参数也是一个函数, 而且这个函数的类型必须是ExecFnType
    function delayExecFn(fn: ExecFnType) {
      setTimeout(() => {
        fn("why", 18)
      }, 1000);
    }
    
    // 3.执行上面函数, 并且传入一个匿名函数
    delayExecFn((name, age) => {
      console.log(name, age)
    })
    
    export {}
    
  • never类型

    never 表示永远不会发生值得类型,比如一个函数:

    如果一个函数中是一个死循环或者一个异常,这个函数不会返回东西,此时可以用never类型

    // 一. 实际开发中只有进行类型推导时, 可能会自动推导出来是never类型, 但是很少使用它
    // 1.一个函数是死循环
    // function foo(): never {
    //   // while(true) {
    //   //   console.log("-----")
    //   // }
    //   throw new Error("1233")
    // }
    // foo()
    
    // 2.解析歌词的工具
    function parseLyric() {
      return []
    }
    
    
    // 二. 封装框架/工具库的时候可以使用一下never
    // 其他时候在扩展工具的时候, 对于一些没有处理的case, 可以直接报错
    function handleMessage(message: string | number | boolean) {
      switch (typeof message) {
        case "string":
          console.log(message.length)
          break
        case "number":
          console.log(message)
          break
        case "boolean":
          console.log(Number(message))
          break
        default:
          const check: never = message
      }
    }
    
    handleMessage("aaaa")
    handleMessage(1234)
    
    // 另外同事调用这个函数
    handleMessage(true)
    
    export {}
    
  • tuple类型

    元组类型和数组的区别:

    数组中通常建议存放相同类型的元素,不同类型的元素不推荐放在数组中。

    元组中每个元素都有自己特性的类型,根据索引值获取到的值可以确定对应的类型;tuple通常可以作为返回的值,在使用的时候会非常方便。(例如useState的hooks)

    // 保存我的个人信息: why 18 1.88
    // 1.使用数组类型
    // 不合适: 数组中最好存放相同的数据类型, 获取值之后不能明确的知道对应的数据类型
    const info1: any[] = ["why", 18, 1.88]
    const value = info1[2]
    console.log()
    
    
    // 2.使用对象类型(最多)
    const info2 = {
    name: "why",
    age: 18,
    height: 1.88
    }
    
    // 3.使用元组类型
    // 元组数据结构中可以存放不同的数据类型, 取出来的item也是有明确的类型
    const info3: [string, number, number] = ["why", 18, 1.88]
    const value2 = info3[2]
    
    
    // 在函数中使用元组类型是最多的(函数的返回值)
    function useState(initialState: number): [number, (newValue: number) => void] {
    let stateValue = initialState
    function setValue(newValue: number) {
     stateValue = newValue
    }
    
    return [stateValue, setValue]
    }
    
    const [count, setCount] = useState(10)
    console.log(count)
    setCount(100)
    
    export {}
    
  • 联合类型和交叉类型

    TS允许我们使用多种运算符,从现有类型中构建新类型

    1. 联合类型 Union Type

    2. 联合类型是由两种或者多种其他类型组成的类型

    3. 表示可以是这些类型中的任何一个值

    4. 联合类型中的每一个类型被称之为联合成员

    5. 使用的时候要进行类型缩小

    // 1.联合类型的基本使用
      // let foo: number | string = "abc"
      // foo = 123
    
      // // 使用的时候要特别的小心
      // if (typeof foo === "string") {
      //   console.log(foo.length)
      // }
    
    
      // 2.举个栗子: 打印id
      function printID(id: number | string) {
        console.log("您的ID:", id)
    
        // 类型缩小
        if (typeof id === "string") {
          console.log(id.length)
        } else {
          console.log(id)
        }
      }
    
      printID("abc")
      printID(123)
    
  1. 交叉类型

    1. 交叉类型标识需要满足多个类型的条件
    2. 交叉类型使用&符号
    3. 交叉类型其实是一个never类型,在开发中,通常是对对象类型进行交叉的
     // 交叉类型: 两种(多种)类型要同时满足 
     type NewType = number & string // 没有意义
    
    interface IKun { name: string age: number }
    
    interface ICoder { name: string coding: () => void }
    
    type InfoType = IKun & ICoder
    
    const info: InfoType = { 
        name: "why",
        age: 18, 
        coding: function() { console.log("coding") } 
        }