重学vue(2, 3)及其生态+TypeScript 之 TypeScript

关于typescript中的介绍就不多说了。

编译TypeScript

我们知道typescript只有编译程javascript时,才能在node或者浏览器上运行,那么如何编译typescript呢?

  • 通过tsc命令。
    • 如果想要执行该命令,我们需要全局安装typescript。这样就可以使用该命令编译ts文件了。他会生成对应名字的js文件,然后我们可以通过node或者浏览器运行该js文件。

  • 通过ts-node库,来将ts文件直接在node中进行运行。
    • 安装ts-node库 npm install ts-node -g

    • 并且它还依赖两个其他的库tslib, @types/node。所以我们可以我们也需要安装npm install tslib @types/node -g

  • 通过webpack配置相关loader来编译ts文件。 需要安装一下这些库

    "html-webpack-plugin": "^5.3.2",
    "ts-loader": "^9.2.3",
    "typescript": "^4.3.5",
    "webpack": "^5.44.0",
    "webpack-cli": "^4.7.2",
    "webpack-dev-server": "^3.11.2"

并且需要执行tsc --init, 来生成'tsconfig.json'文件,不然编译时会报错。 在package.json文件中配置一下脚本。

  "scripts": {
    "build": "webpack",
    "serve": "webpack serve"
  }

webpack的配置

    const path = require('path')
    const HtmlWebpackPlugin = require('html-webpack-plugin')

    module.exports = {
      mode: "development",
      entry: "./src/main.ts",
      output: {
        path: path.resolve(__dirname, "./dist"),
        filename: "bundle.js"
      },
      devServer: {
      },
      resolve: {
        extensions: [".ts", ".js", ".cjs", ".json"]
      },
      module: {
        rules: [
          {
            test: /\.ts$/,
            loader: 'ts-loader'
          }
        ]
      },
      plugins: [
        new HtmlWebpackPlugin({
          template: "./index.html"
        })
      ]
    }

执行npm run serve运行项目即可。

TypeScript类型

TypeScript是JavaScript的一个超级。js中存在的类型在ts中都存在,并且ts也扩展了自己的类型,下面我们就来看一下吧。 需要注意的是基本类型的值可以赋值给对应的包装类型,但是包装类型不能赋值给对应的基本类型。

    const message1: String = 'HelloWorld'
    const message2: string = new String('Hello World') 

any类型:unknown是TypeScript中比较特殊的一种类型,它用于描述类型不确定的变量。

unknown类型只能赋值给any和unknown类型 在某些情况下,我们确实无法确定一个变量的类型,并且可能它会发生一些变化,这个时候我们可以使用any类型(类似于Dart语言中的dynamic类型)。

我们给一个any类型的变量赋值任何的值,比如数字、字符串的值

unknown类型

unknown是TypeScript中比较特殊的一种类型,它用于描述类型不确定的变量。 unknown类型只能赋值给any和unknown类型。any类型可以赋值给任意类型。所以unknown相比于any更安全。

    function foo() {
      return 'abc'
    }

    function bar() {
      return 123
    }

    
    

    let flag = true
    
    let result: unknown 
    if (flag) {
      result = foo()
    } else {
      result = bar()
    }

void类型

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

  • 我们可以将null和undefined赋值给void类型,也就是函数可以返回null或者undefined。

  • 函数我们没有写任何类型,那么它默认返回值的类型就是void的,我们也可以显示的来指定返回值是void。

    function sum(num1: number, num2: number): void {
      console.log(num1 + num2)
      return 'pp' 
    }

    sum(20, 30)

never类型

never 表示永远不会发生值的类型。

比如一个函数:如果一个函数中是一个死循环或者抛出一个异常,那么这个函数会返回东西吗?

不会,那么写void类型或者其他类型作为返回值类型都不合适,我们就可以使用never类型。

tuple类型

tuple是元组类型,很多语言中也有这种数据类型,比如Python、Swift等。

那么tuple和数组有什么区别呢?

  • 首先,数组中通常建议存放相同类型的元素,不同类型的元素是不推荐放在数组中。(可以放在对象或者元组中)。

  • 其次,元组中每个元素都有自己特性的类型,根据索引值获取到的值可以确定对应的类型。

  • 其实他就是固定长度和类型的数组。

    const info: [string, number, number] = ["zh", 20, 0]
    const name = info[0]
    console.log(info.slice(0, 1))

大致了解了tuple,那他将用在什么场景呢?通过下面例子来看一看吧。

简单实现一个useState hook函数,我们都知道useState hook,但会一个数组,其中包括两个元素,第一个元素是传入的值,第二个元素是一个函数,所以我们就可以使用tuple来对其进行约束。

    function useState<T>(state: T) {
    
      let currentState = state
      
      const changeState = (newState: T) => {
        currentState = newState
      }
      
      const tuple: [T, (newState: T) => void] = [currentState, changeState]
      return tuple
    }

    const [counter, setCounter] = useState(10);
    setCounter(1000)

    const [title, setTitle] = useState("abc")

函数的类型约束

函数是JavaScript非常重要的组成部分,TypeScript允许我们指定函数的参数和返回值的类型。

函数类型的定义

    type FooFnType = () => void
    function bar(fn: FooFnType) {
      fn()
    }

参数的类型注解

  • 声明函数时,可以在每个参数后添加类型注解,以声明函数接受的参数类型。

  • 当我们在定义函数参数的时候,必选参数不能位于可选参数后。

    
    function foo(n1?: number, n2: string) {
      return n1 + n2
    }

    foo(9, 'pp')
    
    type objType = {
      name?: string
      age: number
    }

  • 当出现在高阶函数中的传入的函数参数,我们不需要指定类型注解,因为该函数的参数会自动指定类型

    const names = ["abc", "cba", "nba"]
    
    
    names.forEach(function(item) {
      
    })

  • 当传入的类型是一个对象的时候

    • 我们可以这样指定

    function printPoint(point: { x: number; y: number }) {
      console.log(point.x)
      console.log(point.y)
    }
    
    printPoint({ x: 123, y: 321 })
    
    
    • 我们也可以指定对象属性的可选性,通过?来标识

    function printPoint(point: {x: number, y: number, z?: number}) {
      console.log(point.x)
      console.log(point.y)
      console.log(point.z)
    }
    
    printPoint({x: 123, y: 321})
    printPoint({x: 123, y: 321, z: 111})
    
    

返回值的类型注解

  • 声明函数时, 可以在函数列表的后面添加返回值类型注解。

  • 和变量的类型注解一样,我们通常情况下不需要返回类型注解,因为TypeScript会根据 return 返回值推断函数的返回类型。 函数重载

函数重载的定义:

允许创建多个具有不同实现的同名函数。对重载函数的调用会运行其适用于调用上下文的具体实现,即允许一个函数调用根据上下文执行不同的任务。

  • 多个函数定义使用相同的函数名称

  • 函数参数的数量或类型必须有区别 在ts中实现函数重载和其他编程语言不同,它需要先定义函数的重载类型,然后再实现函数。

下面这是错误的实现重载的方式

    

    function p(n1: number, n2: number) {
      return n1 + n2
    }
    function p(n1: string, n2: string) {
      return n1 + n2
    }
    console.log(p(20, 30))

正确实现重载的方式

    
    
    function add(num1: number, num2: number): number; 
    function add(num1: string, num2: string): string;
    
    
    function add(num1: any, num2: any): any {
      if (typeof num1 === 'string' && typeof num2 === 'string') {
        return num1.length + num2.length
      }
      return num1 + num2
    }

    const result = add(20, 30)
    const result2 = add("abc", "cba")

联合类型

有时候一个变量,他可以被赋值为多种类型的值,我们就可以使用|来分割每种类型。

    const id: number|string|boolean = 2;
    console.log('id', id)

其实上面讲到的可选类型,可以看做是 类型undefined 的联合类型

    message?: string ===> message: string | undefined

交叉类型

交叉类型表示需要满足多个类型的条件。我们就可以使用 & 来分割每种类型。

    interface ISwim {
      swimming: () => void
    }

    interface IFly {
      flying: () => void
    }

    type MyType1 = ISwim | IFly 
    type MyType2 = ISwim & IFly

    
    const obj1: MyType1 = {
      
      
      swimming() {},
    }

    const obj2: MyType2 = {
      
      swimming() {},
      flying() {},
    }

在开发中,我们进行交叉时,通常是对对象类型进行交叉的。合并对象

interface Colorful {
  color: string
}
interface IRun {
  running: () => void
}

type NewType = Colorful & IRun

const obj: NewType = {
  color: 'red',
  running: () => {},
}

类型别名

有时候,我们写对象或者比较复杂的类型时。例如传入给一个函数作为参数,看起来比较不好理解,而且复杂。当多个地方都用到同样的对象结构的时候,我们需要写很多遍。所以,这时候我们就可以使用type来为对象定义一个类型别名。让其可复用且容读。

    type IDType = string | number | boolean
    type PointType = {
      x: number
      y: number
      z?: number
    }

    function printId(id: IDType) {

    }

    function printPoint(point: PointType) {

    }

类型断言

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

    class Person {

    }

    class Student extends Person {
      studying() {

      }
    }

    function sayHello(p: Person) {
    
      (p as Student).studying()
    }

    const stu = new Student()
    sayHello(stu)


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

非空类型断言!

当我们编写下面的代码时,在执行ts的编译阶段会报错:这是因为传入的message有可能是为undefined的,这个时候是不能执行方法的;

function printMessageLength(message?: string) {
  console.log(message.length)
}

printMessageLength()

但是,我们确定传入的参数是有值的,这个时候我们可以使用非空类型断言:非空断言使用的是 ! ,表示可以确定某个标识符是有值的,跳过ts在编译阶段对它的检测。如果没有传值,执行是依旧报错。

 function printMessageLength(message?: string) {
  console.log(message!.length)
}

printMessageLength() 

这时候,我们就可以使用es11中可选链使用可选链操作符 ?., 它的作用是当对象的属性不存在时,会短路,直接返回undefined,如果存在,那么才会继续执行。

 function printMessageLength(message?: string) {
  console.log(message?.length)
}

printMessageLength() 

既然讲到了?.,那我们就再来看看??的作用吧?

他是es11新增的。空值合并操作符(??)是一个逻辑操作符,当操作符的左侧是 null 或者 undefined 时,返回其右侧操作数,否则返回左侧操作数。其实他的效果和||一样,但是||的使用本身用在if判断中的,所以以后可以用??来代替||做短路运算。

    let message: string | null = 'Hello World'

    const content1 = message ?? '你好1'
    const content2 = message ? message : '你好2'
    const content3 = message || '你好3'
    console.log(content1)
    console.log(content2)
    console.log(content3)

字面量类型

除了前面我们所讲过的类型之外,也可以使用字面量类型(literal types)。这个类型再能赋值指定的字面量作为值。 他一般和联合类型使用才有意义。

    
    type Alignment = 'left' | 'right' | 'center'

    let align: Alignment = 'left'
    align = 'right'
    align = 'center'


类型缩小

什么是类型缩小呢?

我们可以通过类似于 typeof padding === "number" 的判断语句,来改变TypeScript的执行路径。在给定的执行路径中,我们可以缩小比声明时更小的类型,这个过程称之为 缩小。而我们编写的 typeof padding === "number 可以称之为 类型保护(type guards)。

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

  • typeof

    
    type IDType = number | string
    function printID(id: IDType) {
      if (typeof id === 'string') {
        console.log(id.toUpperCase())
      } else {
        console.log(id)
      }
    }

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

    type Direction = 'left' | 'right' | 'top' | 'bottom'
    function printDirection(direction: Direction) {
      
      if (direction === 'left') {
        console.log(direction)
      }else if() {
        ...
      }
      
      switch (direction) {
        case 'left':
          console.log(direction)
          break
        case 'right':
          console.log(direction)
          break
        case 'top':
          console.log(direction)
          break
        case 'bottom':
          console.log(direction)
          break
      }
    }

  • instanceof, 一般用于类中的判断

    class Student {
      studying() {}
    }

    class Teacher {
      teaching() {}
    }

    function work(p: Student | Teacher) {
      if (p instanceof Student) {
        p.studying()
      } else {
        p.teaching()
      }
    }

    const stu = new Student()
    work(stu)

  • in

    
    type Fish = {
      swimming: () => void
    }

    type Dog = {
      running: () => void
    }

    function walk(animal: Fish | Dog) {
      if ('swimming' in animal) {
        animal.swimming()
      } else {
        animal.running()
      }
      
    }

    const fish: Fish = {
      swimming() {
        console.log('swimming')
      },
    }

    walk(fish)

有关js中类的知识就不介绍了。只介绍一些typescript中对类的扩展。

类的成员修饰符

在TypeScript中,类的属性和方法支持三种修饰符: public、private、protected

  • public 修饰的是在任何地方可见、公有的属性或方法,默认编写的属性就是public的。

  • private 修饰的是仅在同一类中可见、私有的属性或方法。

  • protected 修饰的是仅在类自身及子类中可见、受保护的属性或方法。

只读属性

如果有一个属性我们不希望外界可以任意的修改,只希望确定值后直接使用,那么可以使用readonly。但是只读属性可以在constructor中对其赋值。属性本身不能进行修改, 但是如果它是对象类型, 对象中的属性是可以修改。

    class Person {
      
      
      readonly name: string
      age?: number
      readonly friend?: Person
      constructor(name: string, friend?: Person) {
        this.name = name
        this.friend = friend
      }
    }

    const p = new Person("llm", new Person("jcl"))
    console.log(p.name)
    console.log(p.friend)

    
    
    if (p.friend) {
      
      p.friend.name = "zh"
      
      p.friend.age = 30
    } 
    
    
    


存取器(getter, setter)

在前面一些私有(private)属性我们是不能直接访问的,或者某些属性我们想要监听它的获取(getter)和设置(setter)的过程,这个时候我们可以使用存取器。

    class Person {
      
      private _name: string
      constructor(name: string) {
        this._name = name
      }

      
      
      set name(newName) {
        this._name = newName
      }
      
      get name() {
        return this._name
      }
    }

    const p = new Person('zh')
    p.name = 'llm'
    console.log(p.name)

抽象类

我们知道,继承是多态使用的前提。所以在定义很多通用的调用接口时, 我们通常会让调用者传入父类,通过多态来实现更加灵活的调用方式。

但是,父类本身可能并不需要对某些方法进行具体的实现,所以父类中定义的方法,,我们可以定义为抽象方法

什么是 抽象方法?

在TypeScript中没有具体实现的方法(没有方法体),就是抽象方法。抽象方法,必须存在于抽象类中。 抽象类是使用abstract声明的类。

抽象类有如下的特点:

  • 抽象类是不能被实例化的(也就是不能通过new创建)。

  • 抽象方法必须被子类实现,否则该类必须是一个抽象类。

    
    function makeArea(shape: Shape) {
      return shape.getArea()
    }


    abstract class Shape {
      abstract getArea(): number
    }


    class Rectangle extends Shape {
      private width: number
      private height: number

      constructor(width: number, height: number) {
        super()
        this.width = width
        this.height = height
      }

      getArea() {
        return this.width * this.height
      }
    }

    class Circle extends Shape {
      private r: number

      constructor(r: number) {
        super()
        this.r = r
      }

      getArea() {
        return this.r * this.r * 3.14
      }
    }

    const rectangle = new Rectangle(20, 30)
    const circle = new Circle(10)

    console.log(makeArea(rectangle))
    console.log(makeArea(circle))

类作为类型

类也可以作为一个类型使用

    class Person {
      name: string = "zh"
      eating() {

      }
    }

    const p = new Person()

    
    const p1: Person = {
      name: "llm",
      eating() {

      }
    }

    function printPerson(p: Person) {
      console.log(p.name)
    }

    printPerson(new Person())
    printPerson({name: "llm", eating: function() {}})

接口

接口定义对象类型(只读, 可选)

可以声明对象类型, 并且可以指定可读属性readonly和可选属性?

    interface IInfoType {
      readonly name: string
      age: number,
      fn: () => void
    }

    const info: IInfoType = {
      name: "zh",
      age: 20,
      fn() {
        console.log(name, age)
      }
    }

定义索引类型

我们可以定义相同类型的键值和键名。

    
    interface IndexLanguage {
      [index: number]: string
    }

    const frontLanguage: IndexLanguage = {
      0: "HTML",
      1: "CSS",
      2: "JavaScript",
      3: "Vue"
    }


    interface ILanguageYear {
      [name: string]: number
    }

    const languageYear: ILanguageYear = {
      "C": 1972,
      "Java": 1995,
      "JavaScript": 1996,
      "TypeScript": 2014
    }

接口定义函数类型

我们可以通过interface来定义对象中普通的属性和方法的,实际上它也可以用来定义函数类型。没有方法体,只有方法签名。但是还是通过类型别名来定义函数类型较好。

    
    
    interface CalcFn {
      (n1: number, n2: number): number
    }

    function calc(num1: number, num2: number, calcFn: CalcFn) {
      return calcFn(num1, num2)
    }

    const add: CalcFn = (num1, num2) => {
      return num1 + num2
    }

    calc(20, 30, add)

接口的继承

接口和类一样是可以进行继承的,也是使用extends关键字。并且可以支持多继承。

    interface ISwim {
      swimming: () => void
    }

    interface IFly {
      flying: () => void
    }

    interface IAction extends ISwim, IFly {}

    const action: IAction = {
      swimming() {},
      flying() {},
    }

类可以实现接口

接口定义后,也是可以被类实现的。如果被一个类实现,那么在之后需要传入接口的地方,都可以将这个类传入。这就是面向接口开发。

我们可以通过implements来实现接口。

    interface ISwim {
      swimming: () => void
    }

    interface IEat {
      eating: () => void
    }

    
    class Animal {}

    
    
    class Fish extends Animal implements ISwim, IEat {
      swimming() {
        console.log('Fish Swmming')
      }

      eating() {
        console.log('Fish Eating')
      }
    }

    class Person implements ISwim {
      swimming() {
        console.log('Person Swimming')
      }
    }

    
    function swimAction(swimable: ISwim) {
      swimable.swimming()
    }

    
    swimAction(new Fish())
    swimAction(new Person())

    swimAction({ swimming: function () {} })

interface和type区别

  • 如果是定义非对象类型,通常推荐使用type,比如Direction、Alignment、一些Function。

  • 如果是定义对象类型,那么他们是有区别的:

    • interface 可以重复的对某个接口来定义属性和方法。ts会合并同名的接口,我们在实现的时候,需要实现合并到接口。

        interface IFoo {
          name: string
        }
    
        interface IFoo {
          age: number
        }
    
        
        const foo: IFoo = {
          name: 'zh',
          age: 18,
        }
    
    
    • type定义的是别名,别名是不能重复的。

        
        type IBar = {
          name: string
          age: number
        }
    
        type IBar = {
        }
    
    

引用赋值

当我们引用赋值对象给不同接口的时候,ts内部会处理不同接口类型的差异,不会报错。如果直接将不同接口的对象赋值其他接口,将会报错。

    interface IPerson {
      name: string
      age: number
    }

    const info = {
      name: 'zh',
      age: 20,
      address: '信阳市',
    }

    
    const p: IPerson = info
    
    const p1: IPerson = {
      name: 'zh',
      age: 20,
      address: '信阳市'
    }

枚举类型

通过enum来定义枚举类型。其实他也可以通过联合类型来代替。

  • 枚举类型的值默认是从0开始的递增。

  • 枚举类型(只针对数字枚举)还可以反向取值,利用下标,取出对应的枚举值。

    enum Direction {
      LEFT,
      RIGHT,
      TOP,
      BOTTOM,
    }

    console.log(Direction[2]) 

  • 枚举类型的属性值,可以是数字,也可以是字符串。

  • 不会为字符串枚举成员生成反向映射。

泛型

软件工程的主要目的是构建不仅仅明确和一致的API,还要让你的代码具有很强的可重用性: 比如我们可以通过函数来封装一些API,通过传入不同的函数参数,让函数帮助我们完成不同的操作。

泛型在函数中的使用

以前我们都是在函数定义的时候,给函数参数和返回值做类型约束,但是现在我们可以通过泛型来对其做类型约束。我们在定义的时候给定参数泛型变量,等我们调用函数的时候传入泛型变量类型即可。使参数的类型是也可以参数化。 一般在我们传入函数参数的时候,ts可以自动推断出泛型变量的类型,所以可以不显示的传入泛型类型。

    function sum<Type>(num: Type): Type {
      return num
    }

    
    sum<number>(20)
    sum<{name: string}>({name: "zh"})
    sum<any[]>(["abc"])
    
    
    sum(50)
    sum("abc")

并且,我们可以定义多个泛型。注意:在传入类型的时候要么都传入要么都不传入。

    function foo<T, E, O>(arg1: T, arg2: E, arg3?: O, ...args: T[]) {}
    
    
    foo<number, string, boolean>(10, 'abc', true)

如果在函数中给泛型指定默认类型,我们必须全部指定,要不然会报错。(这是错误的,请忽略)

    
    function foo<T = string, E = string, O = boolean>(
      arg1: T,
      arg2: E,
      arg3?: O,
      ...args: T[]
    ) {}

    foo<number, string, boolean>(10, 'abc', true)

平时在开发中我们可能会看到一些常用的泛型名称:

  • T:Type的缩写,类型

  • K、V:key和value的缩写,键值对

  • E:Element的缩写,元素

  • O:Object的缩写,对象

泛型在接口中的使用

我们可以在接口中使用泛型,来让外界自己传入指定的类型来约束内部的属性或方法。并且他不会自动推断泛型的类型。

    interface IPerson<T1, T2> {
      name: T1
      age: T2
    }

    const p: IPerson<string, number> = {
      name: "zh",
      age: 20
    }

我们也可以指定泛型的默认类型,并且可以部分指定。

    interface IPerson<T1, T2 = number> {
      name: T1
      age: T2
    }
    
    
    const p: IPerson<string> = {
      name: 'zh',
      age: 20,
    }

泛型在类中的使用

我们可以在调用类后面传入类型,也可以在指定实例化对象类型的时候传入类型。并且,如果不传入类型,ts也可以进行类型推断。并且也可以指定默认的类型。

    class Point<T = number> {
      x: T
      y: T
      z: T

      constructor(x: T, y: T, z: T) {
        this.x = x
        this.y = y
        this.z = y
      }
    }

    const p1 = new Point('1.33.2', '2.22.3', '4.22.1')
    const p2 = new Point<string>('1.33.2', '2.22.3', '4.22.1')
    const p3: Point<string> = new Point('1.33.2', '2.22.3', '4.22.1')

泛型约束

有时候我们希望传入的类型有某些共性,但是这些共性可能不是在同一种类型中。这时候我们可以让泛型继承extends自这个类型。

比如string和array都是有length的,或者某些对象也是会有length属性的。那么只要是拥有length的属性都可以作为我们的参数类型,那么应该如何操作呢?

    interface ILength {
      length: number
    }

    function getLength<T extends ILength>(arg: T) {
      return arg.length
    }

    getLength("abc")
    getLength(["abc", "cba"])
    getLength({length: 100})

模块解析

类型查找

之前我们所有的typescript中的类型,几乎都是我们自己编写的,但是我们也有用到一些其他的类型:

    const image = document.getElementById('image') as HTMLImageElement

大家是否会奇怪,我们的HTMLImageElement类型来自哪里呢?甚至是document为什么可以有getElementById的方法呢?

其实这里就涉及到typescript对类型的管理和查找规则了。我们这里先给大家介绍另外的一种typescript文件:.d.ts文件

我们之前编写的typescript文件都是 .ts 文件,这些文件最终会输出 .js 文件,也是我们通常编写代码的地方。还有另外一种文件 .d.ts 文件,它是用来做类型的声明(declare)。 它仅仅用来做类型检测,告知typescript我们有哪些类型。

那么typescript会在哪里查找我们的类型声明呢?

  • 内置类型声明。
    • 内置类型声明是typescript自带的、帮助我们内置了JavaScript运行时的一些标准化API的声明文件。

    • 包括比如Math、Date等内置类型,也包括DOM API,比如Window、Document等。

  • 外部定义类型声明。通常是我们使用一些库(比如第三方库)时,需要的一些类型声明。
    • 有时候安装第三方包,他自己内部就定义了.d.ts文件。例如axios库。

    • 大部分情况下,第三方库都没有提供内置.d.ts文件,我们可以通过该链接[1]查找对应库的.d.ts来进行安装。

  • 自己定义类型声明。 下面我们就来介绍自定义类型声明。

声明模块

什么情况下需要自己来定义声明文件呢?

  • 我们使用的第三方库是一个纯的JavaScript库,没有对应的声明文件。 可以通过声明模块的语法: declare module '模块名' {}
    • 在声明模块的内部,我们可以通过 export 导出对应库的类、函数等。

    declare module 'lodash' {
      export function join(arr: any[]): void
    }

  • 我们给自己的代码中声明一些类型,方便在其他地方直接进行使用。就是将声明变量/函数/类等的类型声明和实现分开写。

    
    declare let name: string
    declare let age: number
    
    declare function foo(): void
    
    declare class Person {
      name: string
      age: number
      constructor(name: string, age: number)
    }

  • 声明文件,typescript也不识别导入的图片文件等,所以我们也需要声明。

    
    declare module '*.jpg'
    declare module '*.jpeg'
    declare module '*.png'
    declare module '*.svg'
    declare module '*.gif'

  • 声明命名空间

    
    declare namespace $ {
      export function ajax(settings: any): any
    }

上面类型的具体实现

  <script src="https://cdn.bootcdn.net/ajax/libs/jquery/3.6.0/jquery.js
  "></script>

    let name = 'zh'
    let age = 20

    function foo() {
      console.log('foo')
    }

    class Person {
      name
      age
      constructor(name, age) {
        this.name = name
        this.age = age
      }
    }
    console.log(name)
    console.log(age)

    foo()

    const p = new Person('llm', 20)
    console.log(p)

    $.ajax({})

  • 24
    点赞
  • 24
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Web面试那些事儿

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值