【JavaScript】TypeScript总结

TypeScript

TypeScript ?

  • 以 JavaScript 为基础构建的语言
  • 一个 JavaScript 的超集
  • 可以在任何支持 JavaScript 的平台中执行(TS 不能被 JS 解析器直接执行,可以编译为任意版本的 JS )
  • TypeScript 扩展了 JavaScript 并添加了类型

TypeScript 增加了什么?

  • 类型
  • 添加 ES 不具备的新特性
  • 丰富的配置选项
  • 强大的开发工具

TypeScript 开发环境搭建

  • 使用 npm 全局安装 typescript:npm i typescript
  • 使用 tsc 对 ts 文件进行编译:tsc xxx.ts

类型声明

  • 类型声明是 TS 非常重要的一个特点

  • 通过类型声明可以指定 TS 中变量(参数、形参)的类型

  • 指定类型后,当为变量赋值时,TS 编译器会自动检查值是否符合类型声明,符合则赋值,否则报错

  • 简而言之,类型声明给变量设置了类型,使得变量只能存储某种类型的值

  • 语法:

     let 变量: 类型
     let 变量: 类型 = 值
     function fn(参数: 类型, 参数: 类型): 返回值类型 {
       ...
     }
    
  • 自动类型判断

    • TS 拥有自动的类型判断机制
    • 当对变量的声明和赋值是同时进行的,TS 编译器会自动判断变量的类型
    • 所以如果你的变量的声明和赋值是同时进行的,可以省略掉类型声明
  • 类型

    • number(任意数字:1,2,3)
    • string(任意字符串:'hi')
    • boolean(布尔值)
    • 字面量(限制变量的值就是该字面量的值)
    • any(任意类型:*)
    • unknown(类型安全的 any:*)
    • void(没有值或 undefined:空值 undefined)
    • never(不能是任何值:没有值)
    • object(任意的 JS 对象:{name:'ABing'})
    • array(任意的 JS 数组:[1,2,3])
    • tuple(元素,TS 新增类型,固定长度数组:[4,5])
    • enum(枚举,TS 中新增类型:enum(A,B))
    // 也可以直接使用字面量进行类型声明
    let a: 10
    a = 10
    
    // 可以使用 | 来连接多个类型(联合类型)
    let b: 'male' | 'female'
    b = 'female'
    
    let c: boolean | string | number
    c = true
    c = 'hello'
    c = 222
    
    // any 标识的是任意类型,一个变量设置类型为any后相当于对该变量关闭了TS的类型检测
    // 使用TS时,不建议使用any类型
    let d: any
    // 声明变量如果不指定类型,则TS解析器会自动判断变量的类型为any(隐式any)
    // let d
    d = 10
    d = 'hello'
    d = true
    
    // unknown 表示未知类型的值
    let e: unknown
    e = 10
    e = 'hello'
    e = true
    
    // any类型,可以赋值给任意变量(建议使用unknown)
    let s: string
    s = d
    
    // unknown实际上就是要给类型安全的any,unknown类型的变量,不能直接赋值给其他变量
    // s = e
    
    // 必须先进行类型判断再进行赋值
    if (typeof e === 'string') {
      s = e
    }
    
    // 或者使用 类型断言:可以用来告诉解析器变量的实际类型
    /**
     * 语法:
     *  变量 as 类型
     *  <类型>变量
     */
    s = e as string
    s = <string>e
    
    // void用来表示空,以函数为例,就表示没有返回值
    function fn(): void {
      return null
    }
    
    // never表示永远不会返回结果
    function fn2(): never {
      throw new Error('error')
    }
    
    // object表示一个js对象
    let a: object
    a = {}
    a = function () {}
    
    // {}用来指定对象中可以包含哪些属性
    // 语法:{属性名:属性值,属性名:属性值}
    // 在属性名后面加上 ? 表示属性时可选属性
    let b: { name: string; age?: number }
    // b = {}
    b = { name: 'ABing' }
    
    // [propName:string]:any 表示任意类型的属性
    let c: { name: string; [propName: string]: any }
    c = { name: 'ABing', age: 20, gender: '男' }
    
    /**
     * 设置函数结构的类型声明
     *  语法:(形参:类型,形参:类型,...) => 返回值
     */
    let d: (a: number, b: number) => number
    d = function (n1: number, n2: number): number {
      return n1 + n2
    }
    
    /**
     * 数组的类型声明:
     *  类型[]
     *  Array<类型>
     */
    // string[]表示字符串数组
    let e: string[]
    e = ['a', 'b']
    
    // number[]表示数值数组
    let f: number[]
    let g: Array<number>
    g = [1, 2, 3]
    
    /**
     * 元组:固定长度的数组(存储效率较数组高)
     * 语法:
     *  [类型,类型,类型]
     */
    let h: [string, number]
    // h = ['1', '2', 3]
    h = ['1', 2]
    
    /**
     * enum枚举
     */
    enum Gender {
      Male,
      Female,
    }
    let i: { name: string; gender: Gender }
    i = {
      name: 'ABing',
      gender: Gender.Male,
    }
    console.log(i.gender === Gender.Male)
    
    // | 表示或,& 表示与
    let j: { name: string } & { age: number }
    j = { name: 'ABing', age: 20 }
    
    // 类型别名
    type myType = 1 | 2 | 3
    let k: myType
    let l: myType
    

编译选项(tsconfig.json)

{
  /* 
  tsconfig.json是ts编译器的配置文件,ts编译器可以根据它的信息来对代码进行编译
  */
  // 用来指定哪些ts文件需要被编译,* 表示任意文件,** 表示任意目录
  "include": ["./src/**/*"],
  /* 
  表示不需要被编译的文件目录
    默认值:["node_modules","bower_components","jspm_packages"]
  */
  "exclude": ["./src/hello/**/*"],
  // extends表示被集成的配置文件
  // "extends": "",
  // files指定被编译文件的列表,类似include,只有需要编译的文件少时才会使用
  // "files": [],
  /* 
  编译器选项
  */
  "compilerOptions": {
    // 用来指定ts被编译为的版本,默认ES3,ESNext表示最新版本ES
    "target": "ESNext",
    // 指定要使用的模块化的规范
    "module": "commonjs",
    // lib用来指定项目中要使用的库
    "lib": ["DOM"],
    // outDir用来指定编译后的文件所在目录
    "outDir": "./dist",
    // 将代码合并为一个文件
    // 设置outFile后,所有的全局作用域中的代码会合并到同一个文件中
    "outFile": "./dist/app.js",
    // 是否对js文件进行编译,默认是false
    "allowJs": false,
    // 是否检查js代码是否符合代码规范,默认false
    "checkJs": false,
    // 是否移除注释
    "removeComments": false,
    // 不生成编译后的文件(适用于只是用tsc检查语法而不生成编译文件的场景)
    "noEmit": false,
    // 当有错误时,不生成编译文件
    "noEmitOnError": false,
    // 所有严格检查,建议设置为true
    "strict": false,
    // 用来设置编译后的文件是否使用 严格模式 ,默认false
    "alwaysStrict": false,
    // 不允许隐式any,默认false
    "noImplicitAny": false,
    // 不允许不明确类型的this,默认false
    "noImplicitThis": false,
    // 严格检查空值
    "strictNullChecks": false
  }
}

搭配 webpack 使用

  • 安装相关依赖:npm i typescript ts-loader -D

  • 配置打包规则

    <!-- webpack.config.js -->
    // 指定要加载的规则
    rules: [
      {
        // test指定规则生效的文件
        test: /\.ts$/,
        // 要使用的loader
        use: 'ts-loader',
        // 要排除的文件
        exclude: /node-modules/,
      },
    ],
    

面向对象

  • 类(class)

    // 使用class关键字来定义一个类
    /**
     * 对象中主要包含两个方法
     *  属性
     *  方法
     */
    class Person {
      /**
       * 直接定义的属性是实例属性,需要通过对象的实例去访问
       *  const per = new Person()
       *  per.name
       *
       * 使用static开头的属性是静态属性(类属性),可以直接通过类去访问
       *  Person.age
       *
       * readonly开头的属性表示一个只读属性,无法修改
       */
    
      // 定义实例属性
      // readonly name: string = 'ABing'
      name = 'ABing'
    
      // 在属性前使用static关键字可以定义类属性(静态属性)
      // static readonly age: number = 20
      age = 20
    
      // 定义方法
      /**
       * 如果方法以static开头则方法就是类方法,可以直接通过类去调用
       * 否则为实例方法,通过实例调用
       */
      sayHello() {
        console.log('Hello')
      }
    }
    
    const per = new Person()
    console.log(per.name)
    
    // 静态属性要通过类去访问
    // console.log(Person.age)
    
    // per.name = 'abing'
    // console.log(per.name)
    
    per.sayHello()
    
  • 构造函数

    class Dog {
      name: string
      age: number
    
      // constructor 构造函数
      // 构造函数会在对象创建时调用
      constructor(name: string, age: number) {
        // 在实例方法中,this就表示当前的实例
        // 在构造函数中,当前对象就是当前新创建的那个对象
        // 可以通过this向新建的对象中添加属性
        this.name = name
        this.age = age
      }
      bark() {
        // 在方法中可以通过this来表示当前调用方法的对象
        console.log(this)
      }
    }
    const dog = new Dog('a', 2)
    const dog2 = new Dog('b', 3)
    
    console.log(dog)
    console.log(dog2)
    
    dog.bark()
    dog2.bark()
    
  • 继承

    ;(function () {
      // 定义一个Animal类
      class Animal {
        name: string
        age: number
    
        constructor(name: string, age: number) {
          this.name = name
          this.age = age
        }
    
        sayHello() {
          console.log(this.name + '在叫')
        }
      }
    
      /**
       * Animal称为父类,Dog、Cat称为子类
       * 使用继承后,子类将会拥有父类所有的方法和属性
       * 通过继承可以将多个类中共有的代码写在一个父类中,这样只需要写一次即可让所有的子类都同时拥有父类中的属性
       * 如果希望在子类中添加一些父类中没有的属性或方法可以直接添加
       * 如果在子类中添加了和父类相同的方法,则子类方法会覆盖父类方法(这种子类覆盖父类方法的形式,我们叫做方法的重写)
       */
      // 定义一个表示狗的类并继承Animal类
      class Dog extends Animal {
        run() {
          console.log(this.name + '在跑')
        }
        sayHello() {
          console.log('汪汪汪汪!')
        }
      }
      // 定义一个表示猫的类并继承Animal类
      class Cat extends Animal {}
    
      const dog = new Dog('旺财', 5)
      const cat = new Cat('咪咪', 5)
      console.log(dog)
      dog.sayHello()
      dog.run()
      console.log(cat)
      cat.sayHello()
    })()
    
  • super 关键字

    ;(function () {
      class Animal {
        name: string
    
        constructor(name: string) {
          this.name = name
        }
        sayHello() {
          console.log('动物在叫')
        }
      }
    
      class Dog extends Animal {
        age: number
    
        constructor(name: string, age: number) {
          // 如果在子类中写了构造函数,在子类的构造函数中必须对父类的构造函数进行调用
          super(name) // 调用父类的构造函数
          this.age = age
        }
        sayHello() {
          // 在类的方法中,super就表示当前类的父类
          super.sayHello()
        }
      }
    
      const dog = new Dog('旺财', 3)
      dog.sayHello()
    })()
    
  • 抽象类

    ;(function () {
      /**
       * 以abstract开头的类是抽象类
      *  抽象类和其他类区别不大,只是不能用来创建对象
      *  抽象类就是专门用来继承的类
      *
      *  抽象类中可以添加抽象方法
      */
      abstract class Animal {
        name: string
    
        constructor(name: string) {
          this.name = name
        }
        /**
         * 定义一个抽象方法
        * 抽象方法使用abstract开头,没有方法体
        * 抽象方法只能定义在抽象类中,子类必须对抽象方法进行重写
        */
        abstract sayHello(): void
      }
    
      class Dog extends Animal {
        sayHello() {
          console.log('汪汪汪汪!')
        }
      }
    
      class Cat extends Animal {
        sayHello() {
          console.log('喵喵喵!')
        }
      }
    
      const dog = new Dog('旺财')
      dog.sayHello()
    })()
    
  • 接口

    ;(function () {
      // 描述一个对象的类型
      type myType = {
        name: string
        age: number
      }
    
      // const obj: myType = {
      //   name: 'ABing',
      //   age: 20,
      // }
    
      /**
       * 接口用来定义一个类结构,用来定义一个类中应该包含哪些属性和方法,同时接口可以当成类型声明去使用
      */
      interface myInterface {
        name: string
        age: number
      }
    
      interface myInterface {
        gender: string
      }
    
      const obj: myInterface = {
        name: 'ABing',
        age: 20,
        gender: 'male',
      }
    
      /**
       * 接口可以在定义类的时候去限制类的结构
      * 接口中所有的属性都不能有实际的值
      * 接口只定义对象的结构,而不考虑实际值
      *  在接口中所有的方法都是 抽象方法
      */
      interface myInter {
        name: string
        sayHello(): void
      }
    
      /**
       * 定义类时,可以使类去实现一个接口
      * 实现接口就是使类满足接口的要求
      */
      class MyClass implements myInter {
        name: string
        constructor(name: string) {
          this.name = name
        }
        sayHello() {
          console.log('Hello')
        }
      }
    
      const myClass = new MyClass('ABing')
      myClass.sayHello()
    })()
    
  • 泛型

    /**
     * 在定义函数或类时,如果遇到类型不明确时就可以使用泛型
    */
    function fn<T>(a: T): T {
      return a
    }
    
    // 可以直接调用具有泛型的函数
    // 不指定泛型,TS可以自动对类型进行推断
    fn(10)
    // 指定泛型
    fn<string>('hello')
    
    // 泛型可以同时指定多个
    function fn2<T, K>(a: T, b: K): T {
      return a
    }
    fn2<number, string>(123, 'hello')
    
    interface Inter {
      length: number
    }
    
    // 表示泛型T必须是Inter实现类(子类)
    function fn3<T extends Inter>(a: T): number {
      return a.length
    }
    fn3('222')
    
    class MyClass<T> {
      name: T
      constructor(name: T) {
        this.name = name
      }
    }
    const mc = new MyClass<string>('ABing')
    
posted @ 2020-12-22 17:32  [ABing]  阅读(196)  评论(0编辑  收藏  举报