Typescript 数据类型

134 阅读17分钟

TS数据类型

笔记来源:coderwhy课程

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

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

支持JS数据类型

      ```TypeScript
    // 数字 布尔 字符串类型 ---这些用法和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

      •     联合类型是由两种或者多种其他类型组成的类型
        
      •     表示可以是这些类型中的任何一个值
        
      •     联合类型中的每一个类型被称之为联合成员
        
      •     使用的时候要进行**类型缩小**
        
          // 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)
      
    2. 交叉类型

      • 相关知识 类型别名:type interface

        •         给**对象类型**起一个别名--复用,可在多个地方使用
          
        •         type是有个`=` interface不是定义,没有`=`,接口是声明的方式,直接interface 别名 {}
          
        •         区别如下:
          
                     类型别名和接口非常相似,在定义**对象类型**时,大多时候可以任意选择使用
          
                     接口中几乎所有特性都几乎可以在type中使用
          
        // 1.区别一: type类型使用范围更广(基础类型等都可用), 接口类型只能用来声明对象
        type MyNumber = number
        type IDType = number | string
        
        
        // 2.区别二: 在声明对象时, interface可以多次声明
        // 2.1. type不允许两个相同名称的别名同时存在
        // type PointType1 = {
        //   x: number
        //   y: number
        // }
        
        // type PointType1 = {
        //   z?: number
        // }
        
        
        // 2.2. interface可以多次声明同一个接口名称
        interface PointType2 {
          x: number
          y: number
        }
        
        interface PointType2 {
          z: number
        }
        
        const point: PointType2 = {
          x: 100,
          y: 200,
          z: 300
        }
        
        
        // 3.interface支持继承的
        interface IPerson {
          name: string
          age: number
        }
        
        interface IKun extends IPerson {
          kouhao: string
        }
        
        const ikun1: IKun = {
          kouhao: "你干嘛, 哎呦",
          name: "kobe",
          age: 30
        }
        
        // 4.interface可以被类实现(TS面向对象时候再讲)
        // class Person implements IPerson {
        
        // }
        
        
        // 总结: 如果是非对象类型的定义使用type, 如果是对象类型的声明那么使用interface
        
        
        export {}
        
  •     交叉类型标识需要满足多个类型的条件
    
  •     交叉类型使用`&`符号
    
  •     交叉类型其实是一个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")
                  }
                }
    
  • 类型断言as和非空类型断言

    1. 类型断言as

      有时候TypeScript无法获取具体的类型信息,这个我们需要使用类型断言(Type Assertions)。

      比如我们通过 document.getElementById,TypeScript只知道该函数会返回 HTMLElement ,但并不知道它具体的类型:

          // 获取DOM元素 <img class="img"/>
          // const imgEl = document.querySelector(".img")
          // if (imgEl !== null) { // 类型缩小
          //   imgEl.src = "xxx"
          //   imgEl.alt = "yyy"
          // }
      
          // 使用类型断言,在获取的时候直接确定获取的dom元素是图片元素
          const imgEl = document.querySelector(".img") as HTMLImageElement
          imgEl.src = "xxx"
          imgEl.alt = "yyy"
      

      TypeScript只允许类型断言转换为 更具体 或者 不太具体 的类型版本,此规则可防止不可能的强制转换:

            // 类型断言的规则: 断言只能断言成更加具体的类型, 或者 不太具体(any/unknown) 类型
            const age: number = 18
            // 错误的做法
            // const age2 = age as string
      
            // TS类型检测来说是正确的, 但是这个代码本身不太正确
            // const age3 = age as any
            // const age4 = age3 as string
            // console.log(age4.split(" "))
      
            export {}
      
    2. 非空类型断言

        // 定义接口
        interface IPerson {
          name: string
          age: number
          friend?: {
            name: string
          }
        }
      
        const info: IPerson = {
          name: "why",
          age: 18
        }
      
        // 访问属性: 可选链: ?.
        console.log(info.friend?.name)
      
        // 属性赋值:不能用可选链
        // 解决方案一: 类型缩小
        if (info.friend) {
          info.friend.name = "kobe"
        }
      
        // 解决方案二: 非空类型断言(有点危险, 只有确保friend一定有值的情况, 才能使用)
        info.friend!.name = "james"
      
        export {}
      
  • 字面量类型和类型缩小

    1. 字面量类型

        // 1.字面量类型的基本上
        const name: "why" = "why"
        let age: 18 = 18
      
        // 2.将多个字面量类型联合起来 |
        //起到了类似枚举的作用,只能在其中类型选择 
        type Direction = "left" | "right" | "up" | "down"
        const d1: Direction = "left"
      
        // 栗子: 封装请求方法
        type MethodType = "get" | "post"
        function request(url: string, method: MethodType) {
        }
      
        request("http://codercba.com/api/aaa", "post")
      
        // TS细节
        // const info = {
        //   url: "xxxx",
        //   method: "post"
        // }
        // 下面的做法是错误: info.method获取的是string类型
        //因为我们的对象在进行字面量推理的时候,info其实是一个 {url: string, method: string},所以我们没办法将一个 string 赋值给一个 字面量 类型。
        // request(info.url, info.method)
      
        // 解决方案一: info.method进行类型断言
        // request(info.url, info.method as "post")
      
        // 解决方案二: 直接让info对象类型是一个字面量类型
        // const info2: { url: string, method: "post" } = {
        //   url: "xxxx",
        //   method: "post"
        // }
        const info2 = {
          url: "xxxx",
          method: "post"
        } as const
        // xxx 本身就是一个string
        request(info2.url, info2.method)
      
        export {}
      
    2. 类型缩小

      • 什么是类型缩小呢?

      类型缩小的英文是 Type Narrowing(也有人翻译成类型收窄);

       我们可以通过类似于 typeof padding === "number" 的判断语句,来改变TypeScript的执行路径;

       在给定的执行路径中,我们可以缩小比声明时更小的类型,这个过程称之为 缩小( Narrowing );

       而我们编写的 typeof padding === "number 可以称之为 类型保护(type guards);

      • 常见的类型保护有如下几种:

         typeof

         平等缩小(比如===、!==)

         instanceof

         in

        ... 等等

      • typeof

        在 TypeScript 中,检查返回的值typeof是一种类型保护:

         因为 TypeScript 对如何typeof操作不同的值进行编码。

        // 1.typeof: 使用的最多
        function printID(id: number | string) {
          if (typeof id === "string") {
            console.log(id.length, id.split(" "))
          } else {
            console.log(id)
          }
        }
        
      • 平等缩小

        我们可以使用Switch或者相等的一些运算符来表达相等性(比如===, !==, ==, and != ):

        // 2.===/!==: 方向的类型判断
        type Direction = "left" | "right" | "up" | "down"
        function switchDirection(direction: Direction) {
          if (direction === "left") {
            console.log("左:", "角色向左移动")
          } else if (direction === "right") {
            console.log("右:", "角色向右移动")
          } else if (direction === "up") {
            console.log("上:", "角色向上移动")
          } else if (direction === "down") {
            console.log("下:", "角色向下移动")
          }
        
      • instanceof

        JavaScript 有一个运算符来检查一个值是否是另一个值的“实例”:

        // 3. instanceof: 传入一个日期, 打印日期
        function printDate(date: string | Date) {
          if (date instanceof Date) {
            console.log(date.getTime())
          } else {
            console.log(date)
          }
        
          //typeof Date是object
          // if (typeof date === "string") {
          //   console.log(date)
          // } else {
          //   console.log(date.getTime())
          // }
        }
        
      • in操作符

        Javascript 有一个运算符,用于确定对象是否具有带名称的属性:in运算符

         如果指定的属性在指定的对象或其原型链中,则in运算符返回true

        // 4.in: 判断是否有某一个属性
        interface ISwim {
          swim: () => void
        }
        
        interface IRun {
          run: () => void
        }
        
        function move(animal: ISwim | IRun) {
          if ("swim" in animal) {
            animal.swim()
          } else if ("run" in animal) {
            animal.run()
          }
        }
        
        const fish: ISwim = {
          swim: function() {}
        }
        
        const dog: IRun = {
          run: function() {}
        }
        
        move(fish)
        move(dog)
        
  • 函数类型

    1. 函数的类型

      ◼ 在JavaScript开发中,函数是重要的组成部分,并且函数可以作为一等公民(可以作为参数,也可以作为返回值进行传递)。

      ◼ 那么在使用函数的过程中,函数是否也可以有自己的类型呢?

      function foo(arg: number): number {
        return 123
      }//声明的写法
      
      // foo本身也是一个标识符(对象), 也应该有自己的类型
      const bar: any = (arg: number): number => {
        return 123
      }//表达式的写法
      
      function delayExecFn(fn) {
      
      }
      

      ◼ 我们可以编写函数类型的表达式(Function Type Expressions),来表示函数类型;

      // 方案一: 函数类型表达式 function type expression
      // 格式: (参数列表) => 返回值,函数列表必须先写形参名字,再赋类型
      type BarType = (num1: number) => number
      const bar: BarType = (arg: number): number => {
        return 123
      }
      
      export {}
      
    2. 函数类型解析

      在上面的语法中 (num1: number, num2: number) => void,代表的就是一个函数类型:

       接收两个参数的函数:num1和num2,并且都是number类型;

       并且这个函数是没有返回值的,所以是void;

      type CalcType = (num1: number, num2: number) => number
      
      // 1.函数的定义
      function calc(calcFn: CalcType) {
        const num1 = 10
        const num2 = 20
        const res = calcFn(num1, num2)
        console.log(res)
      }
      
      
      // 2.函数的调用
      function sum(num1: number, num2: number) {
        return num1 + num2
      }
      
      function foo(num1: number) {
        return num1
      }
      calc(sum)
      calc(foo)
      
      function mul(num1: number, num2: number) {
        return num1 * num2
      }
      calc(mul)
      
      // 3.使用匿名函数
      calc(function(num1, num2) {
        return num1 - num2
      })
      
      export {}
      

      注意:在某些语言中,可能参数名称num1和num2是可以省略,但是TypeScript是不可以的。

      // TypeScript对于传入的函数类型的多余的参数会被忽略掉(the extra arguments are simply ignored.)
      type CalcType = (num1: number, num2: number) => number
      function calc(calcFn: CalcType) {
        calcFn(10, 20)
      }
      
      calc(function(num) {
        return 123
      })
      
      // forEach栗子:
      const names = ["abc", "cba", "nba"]
      names.forEach(function(item) {
        console.log(item.length)
      })
      
      // TS对于很多类型的检测报不报错, 取决于它的内部规则
      // TS版本在不断更新: 在进行合理的类型检测的情况, 让ts同时更好用(好用和类型检测之间找到一个平衡)
      // 举一个栗子:
      interface IPerson {
        name: string
        age: number
      }
      
      // typescript github issue, 成员
      const p = {
        name: "why",
        age: 18,
        height: 1.88,
        address: "广州市"
      }
      
      const info: IPerson = p
      
      export {}
      
    3. 调用签名(Call Signatures)

      在 JavaScript 中,函数除了可以被调用,(函数也是个对象)自己也是可以有属性值的。

       然而前面讲到的函数类型表达式并不能支持声明属性;

       如果我们想描述一个带有属性的函数,我们可以在一个对象类型中写一个调用签名(call signature);

         // 1.函数类型表达式
         type BarType = (num1: number) => number
      
         // 2.函数的调用签名(从对象的角度来看待这个函数, 也可以有其他属性)
         interface IBar {
           name: string
           age: number
           // 函数可以调用: 函数调用签名,函数列表
           (num1: number): number
         }
      
         const bar: IBar = (num1: number): number => {
           return 123
         }
      
         bar.name = "aaa"
         bar.age = 18
         bar(123)
      
      
         // 开发中如何选择:
         // 1.如果只是描述函数类型本身(函数可以被调用), 使用函数类型表达式(Function Type Expressions)
         // 2.如果在描述函数作为对象可以被调用, 同时也有其他属性时, 使用函数调用签名(Call Signatures)
      
      
         export {}
      

      注意这个语法跟函数类型表达式稍有不同,在参数列表和返回的类型之间用的是:而不是=>

    4. 构造签名

      JavaScript 函数也可以使用 new 操作符调用,当被调用的时候,TypeScript 会认为这是一个构造函数(constructors),因为他们会产生一个新对象

       你可以写一个构造签名( Construct Signatures ),方法是在调用签名前面加一个 new 关键词;

             class Person {
             }
      
             interface ICTORPerson {
               //构造前面
               new (): Person
             }
      
             function factory(fn: ICTORPerson) {
               const f = new fn()
               return f
             }
      
             factory(Person)
      
    5. 参数的可选类型

      我们可以指定某个参数是可选的:

      ◼ 这个时候这个参数y依然是有类型的,它是什么类型呢? number | undefined

      ◼ 另外可选类型需要在必传参数的后面

        // y就是一个可选参数,调用的时候可以传递也可以不传递
        // 可选参数类型是什么? number | undefined 联合类型
        function foo(x: number, y?: number) {
          if (y !== undefined) {
            console.log(y + 10)
          }
        }
      
        foo(10)
        foo(10, 20)
      
        export {}
      
    6. 默认参数

      ◼ 从ES6开始,JavaScript是支持默认参数的,TypeScript也是支持默认参数的:

      ◼ 这个时候y的类型其实是 undefined 和 number 类型的联合。

        // 函数的参数可以有默认值
        // 1.有默认值的情况下, 参数的类型注解可以省略
        // 2.有默认值的参数, 是可以接收一个undefined的值
        function foo(x: number, y = 100) {
          console.log(y + 10)
        }
      
        foo(10)
        foo(10, undefined)
        foo(10, 55)
      
        export {}
      
    7. 剩余参数

      function foo(...args: (string | number)[]) {
      
      }
      
      foo(123, 321)
      foo("abc", 111, "cba")
      
    8. 函数的重载(了解)

      在TypeScript中,我们可以去编写不同的重载签名(overload signatures)来表示函数可以以不同的方式进行调用;

      // 需求: 只能将两个数字/两个字符串进行相加
      // 案例分析: any实现
      // function add(arg1, arg2) {
      //   return arg1 + arg2
      // }
      
      // add(10, 20)
      // add("abc", "cba")
      // add({aaa: "aaa"}, 123)
      
      
      // 1.实现两个函数
      // function add1(num1: number, num2: number) {
      //   return num1 + num2
      // }
      
      // function add2(str1: string, str2: string) {
      //   return str1 + str2
      // }
      
      // add1(10, 20)
      // add2("abc", "cba")
      
      
      // 2.错误的做法: 联合类型是不可以
      // function add(arg1: number|string, arg2: number|string) {
      //   return arg1 + arg2
      // }
      
      
      // 3.TypeScript中函数的重载写法
      // 3.1.先编写重载签名
      function add(arg1: number, arg2: number): number
      function add(arg1: string, arg2: string): string
      
      // 3.2.编写通用的函数实现
      function add(arg1: any, arg2: any): any {
      return arg1 + arg2
      }
      
      add(10, 20)
      add("aaa", "bbb")
      // 通用函数不能被调用
      // add({name: "why"}, "aaa")
      // add("aaa", 111)
      
      export {}
      

      一般是编写两个或者以上的重载签名,再去编写一个通用的函数以及实现;

      // 1.普通的实现
      // function getLength(arg) {
      //   return arg.length
      // }
      
      // 2.函数的重载
      // function getLength(arg: string): number
      // function getLength(arg: any[]): number
      // function getLength(arg) {
      //   return arg.length
      // }
      
      
      // 3.联合类型实现(可以使用联合类型实现的情况, 尽量使用联合类型)
      // function getLength(arg: string | any[]) {
      //   return arg.length
      // }
      
      // 4.对象类型实现
      function getLength(arg: { length: number }) {
      return arg.length
      }
      
      
      getLength("aaaaa")
      getLength(["abc", "cba", "nba"])
      getLength({ name: "why", length: 100 })
      
    9. 可推导的this类型

      TypeScript在编译时,认为我们的this是可以正确去使用的:

       这是因为在没有指定this的情况,this默认情况下是any类型的;

        // 在没有对TS进行特殊配置的情况下, this是any类型
      
        // 1.对象中的函数中的this
        const obj = {
          name: "why",
          studying: function() {
            // 默认情况下, this是any类型
            console.log(this.name.length, "studying")
          }
        }
      
        obj.studying()
        // obj.studying.call({})
      
      
        // 2.普通的函数
        function foo() {
          console.log(this)
        }
      
        export {}
      

      this的相关工具

        function foo(this: { name: string }, info: {name: string}) {
          console.log(this, info)
        }
      
        type FooType = typeof foo
      
        // 1.ThisParameterType: 获取FooType类型中this的类型
        type FooThisType = ThisParameterType<FooType>
      
      
        // 2.OmitOmitThisParameter: 删除this参数类型, 剩余的函数类型
        type PureFooType = OmitThisParameter<FooType>
      
      
        // 3.ThisType: 用于绑定一个上下文的this
        interface IState {
          name: string
          age: number
        }
      
        interface IStore {
          state: IState
          eating: () => void
          running: () => void
        }
      
        const store: IStore & ThisType<IState> = {
          state: {
            name: "why",
            age: 18
          },
          eating: function() {
            console.log(this.name)
          },
          running: function() {
            console.log(this.name)
          }
        }
      
        store.eating.call(store.state)
      
      
        export {}