• typescript


    一、安装和使用

    • 下载 node
    • 下载typescript(npm i -g typescript
    • tsc -v 查看 typescript版本
    • tsx XXX.ts 编译 ts 文件,生成对应的 js 文件

    注意:如果生成了 js 文件,ts 文件有变量重复声明的报错,可以执行tsc -init命令,会生成一个 tsconfig.js 文件,如果有 error TS2403: Subsequent variable declarations must have the same type. Variable 'AbortSignal' must be of type '{ new (): AbortSignal; prototype: AbortSignal; abort(reason?: any): AbortSignal; timeout(milliseconds: number): AbortSignal; }', but here has type '{ new (): AbortSignal; prototype: AbortSignal; }'.,或者declare var AbortSignal,或者Type 'Server' is not generic.报错,可以执行 npm install @types/node -D

    二、类型声明

    • 类型声明
      · 类型声明时 TS 非常重要的一个特点
      · 通过类型声明可以指定 TS 中变量(参数、形参)的类型
      · 指定类型后,当为变量赋值时,TS 编译器会自动检查值是否符合类型声明,符合则赋值,否则报错
      · 简而言之,类型声明给变了设置了类型,使得变量只能存储某种类型的值
    • 自动类型判断
      · TS 拥有自动的类型判断机制
      · 当对变量的声明和赋值是同时进行的,TS 编译器会自动判断变量的类型
      · 所以如果变量的声明和赋值是同时进行的,可以省略掉类型声明

    1、变量

    • 声明一个变量a,同时指定它的类型为 number
    • 后续赋值或使用时,类型必须跟声明时的类型一致,否则会报错
    • 如果变了的声明和赋值时同时进行的,可以省略类型,ts 会自动对变量进行类型检测
    • 后续使用或赋值,同样要跟之前的类型一致,否则会报错
    //声明一个变量a,同时指定它的类型为 number
    //后续赋值或使用时,类型必须跟声明时的类型一致,否则会报错
    let a : number;
    a = 1;
    
    //如果变了的声明和赋值时同时进行的,可以省略类型,ts 会自动对变量进行类型检测
    //后续使用或赋值,同样要跟之前的类型一致,否则会报错
    let b = true;
     b = false;
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9

    2、函数

    • 声明函数时,可以指定参数的类型
    • 调用函数时,入参类型要跟声明时的类型一致,另外,入参个数也要和函数声明时接收的参数个数一致
    //声明函数时,可以指定参数的类型
    //调用函数时,入参类型要跟声明时的类型一致,另外,入参个数也要和函数声明时接收的参数个数一致
    //函数声明时,括号后面也可以规定类型,该类型是函数返回值的类型
    
    function sum (a: number, b: number): number{
      return a + b
    }
    
    sum(123,456)
    // sum(123,"456")·//报错
    // sum(123,456,789)·//报错
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11

    三、类型

    在这里插入图片描述

    1、any和unknown的区别

    let a : number;
    a = 1;
    let b = true;
    b = false;
    
    let e : any;
    e = "12"
    a = e
    
    let f : unknown;
    f = "1"
    b = unknown //报错
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 任何类型的变量都可以被赋值为 any 类型,比如上面代码中的 a = e(不仅会把 e 的类型检测关掉,还会把 a 的类型检测也关掉),但是 unkown 不行,unkown类型的数据 不能赋值给其他类型的变量
    • unknown 实际上是一个类型安全的 any

    2、定义对象类型

    //{}用来指定对象中可以包含哪些属性
    //语法:{属性名:属性值,属性名:属性值}
    //在属性名后加上?,表示属性是可选的,但是需要在定义时罗列出来
    //使用[propname:string]:any,是上面?的简写,不需要一一罗列
    //定义对象
    
    let obj : {name: string, age? :number};
    obj = {name: "张三",age: 18}
    obj = {name: "张三"}
    
    // let obj : {name: string,[propname:string]:any};
    // obj = {name: "张三",age: 18, sex: "男"}
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12

    3、定义数组

    //设置数组的类型声明
    //数组的类型声明:
    //  类型[]
    //  Array<类型>
    
    // let arr: string[];   //表示字符串数组
    // arr = ["1","2"]
    // let arr: number[];   //表示数字类型数组
    // arr = [1,2]
    let arr: Array<string>;   //表示字符串数组
    arr = ["1","2"]
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11

    4、定义函数

    //设置函数结构的类型声明:
    //语法:(形参:类型,形参:类型......) => 返回值
    // let d: () => any
    let d: (a: number, b: number) => number
    d = function (a, b) {
      return a + b;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    类型断言

    可以用来告诉解析器的实际类型。
    语法:

    • 变量 as 类型(a as string)
    • <类型>变量(a)
    let a : number;
    a = 1;
    let f : unknown;
    f = 1
    a = f as number
    
    • 1
    • 2
    • 3
    • 4
    • 5

    四、编译选项

    1、自动编译文件

    编译文件时,使用 -w 指令后,TS 编译器会自动监视文件的变化,并在文件变化时对文件进行重新编译,生成对应的 js 文件。
    示例:

    $ tsc demo.ts -w	//监听某个ts文件并进行编译
    $ tsc -w 	//检测整个目录中的ts文件并进行编译
    
    • 1
    • 2

    2、自动编译整个项目

    • 如果直接使用 tsc 指令,则可以自动将当前项目下的所有 ts 文件编译为 js 文件
    • 但是能直接使用 tsc 命令的前提是,要先在项目目录下创建 ts 的配置文件 tsconfig.json(可手动新建文件,也可使用tsc -init命令创建)
    • tsconfig.json 是一个 JSON 文件,添加配置文件后,只需要 tsc 命令即可完成对整个项目的编译
    1.配置选项
    1)include
    • 定义希望被编译文件所在的目录
    • 默认值:["**/*"](** 表示任意目录,* 表示任意文件)
    • 示例:
    {
      //src目录和test目录下的文件都会被编译
      "include": [
        "src/**/*",
        "test/**/*"
      ]
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    2)exclude
    • 和include相反,定义需要排除在外的目录
    • 默认值:["node-modules","bower_componts","jspm_packages"]
    • 示例:
    {
      //目录src下的所有文件都需要编译
      "include": [
        "src/**/*",
        // "test/**/*"
      ],
      //目录test下的所有文件都不需要编译
      "exclude": [
        "test/**/*"
      ]
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    3)extends
    • 定义被继承的配置文件(相当于引入一个配置文件)
    • 示例:
    //当前配置的文件中会自动包含 config 目录下的 base.json 中的所有配置信息
    "extends": "./configs/base"
    
    • 1
    • 2
    4)files
    • 指定被编译文件的列表,只有需要编译的文件少时才会用到(与 include 类似,只是 include 要配置目录,files 要配置文件)
    • 示例:
    "files": [
    	"core.ts",
    	"sys.ts",
    	...
    ]
    
    • 1
    • 2
    • 3
    • 4
    • 5
    5)compilerOptions
    • 编译选项是配置文件中非常重要也比较复杂的配置选项。
    • 在 compilerOptions 中包含多个子选项,用来完成对编译的配置
    "compilerOptions": {
      // target 用来指定 ts 被编译为的 ES 版本
      //'es3', 'es5', 'es6', 'es2015', 'es2016', 'es2017', 'es2018', 'es2019', 'es2020', 'es2021'...
      "target": "ES5",
      // module 指定要使用的模块化的规范
      //“none","commonjs","amd","es6"...
      "module": "commonjs",
      // lib 指定代码运行时所包含的库(宿主环境)
      "lib": [
        "ES5",
        "DOM"
      ],
      // outDir 用来指定编译后文件所在的目录
      "outDir": "./dist",
      // outFile 将所有全局作用域中的代码合并到同一个文件中
      // "outFile": "./dist/app.js"
      // allowJs 师傅对 js 文件进行编译,默认是false
      "allowJs": true,
      // checkJs 是否以 ts 的标准去检查 js 代码是否符合语法规范,默认是 false
      "checkJs": true,
      // removeComments 生成的 js 文件是否需要移除注释
      "removeComments": true,
      // noEmit 执行编译过程,但是不生产编译后的文件,可用作代码检查
      "noEmit": true,
    
      // strict 下面所有严格检查的总开关
      "strict": true,
      // noEmitOnError 当有错误时,不生成编译后的文件
      "noEmitOnError": false,
      // alwaysStrict 用来设置编译后的文件是否使用严格模式
      "alwaysStrict": true,
      // noImplicitAny 不允许隐式的any类型
      "noImplicitAny": true,
      // noImplicitThis 不允许不明确类型的 this
      "noImplicitThis": true,
      // strictNullChecks 严格检查空值
      "strictNullChecks": true
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38

    五、使用webpac打包ts代码

    1.初始化项目

    进入根目录,执行命令npm init -y,命令执行完会自动创建一个 package.json 文件

    2.下载构建工具

    npm i -D webpack webpack-cli typescript ts-loader
    
    • 1

    3.根目录下创建 webpack.config.js 文件

    配置打包的入口、出口和tsloader

    const path = require("path")
    
    module.exports = {
      entry: "./src/main.ts",
      output: {
        path: path.resolve(__dirname, "dist"),
        filename: "bundle.js",
        clean: true
      },
      module: {
        rules: [
          {
            test: /\.ts$/,
            use: "ts-loader",
            exclude: /node_modules/
          }
        ]
      },
      mode: "production"
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20

    4.根目录创建 tsconfig.json 文件,配置基本信息

    {
      "compilerOptions": {
        "target": "ES2015",
        "module": "ES2015",
        "strict": true
      }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

    5.修改 package.json 文件,配置打包命令

      "scripts": {
        "build": "webpack"
      },
    
    • 1
    • 2
    • 3

    6.根目录下的 src 文件中,创建 main.ts 文件

    function sum (a: number, b: number): number {
      return a + b
    }
    console.log(sum(123,456));
    
    • 1
    • 2
    • 3
    • 4

    7.执行打包命令

    npm run build
    命令执行完,会在根目录下生成 dist/bundle.js 文件

    六、面向对象

    1、类

    • 直接定义的属性是实例属性,需要通过对象的实例去访问:
      let per = new Person();
      per.name

    • 使用 static 开头的属性(方法)是静态属性(方法),或者类属性(方法),可以直接通过类去访问:
      Person.job;
      Person.sleep

    • readonly 开头的属性表示是只读属性,无法被修改

    /*
    直接定义的属性是实例属性,需要通过对象的实例去访问:
      let per = new Person();
      per.name
    
    使用 static 开头的属性(方法)是静态属性(方法),或者类属性(方法),可以直接通过类去访问:
      Person.job;
      Person.sleep
    
    readonly 开头的属性表示是只读属性,无法被修改
    */
    
    class Person {
      name: string = "张三";
      age: number = 18
      readonly sex = "男"
    
      static job: string = "程序员";
      eat() {
        return "吃饭";
      }
      static sleep() {
        return "睡觉";
      }
    }
    let per = new Person()
    console.log(per.name);
    console.log(per.age);
    console.log(Person.job);
    console.log(per.eat());
    console.log(Person.sleep());
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31

    2、构造函数和 this

    class Dog{
      name: string
      age: number
      //constructor 被称为构造函数
      //构造函数会在对象创建时调用
      constructor(name: string, age: number) {
        //示例方法中,this表示当前的示例
        //在构造函数中,当前对象就是当前创建的那个对象
        //可以通过 this 向新建的对象中添加属性
        this.name = name
        this.age = age
      }
      eat() {
        return "吃饭"
      }
    }
    let dog1 = new Dog("小黑",5)
    let dog2 = new Dog("小白", 6)
    console.log(dog1);
    console.log(dog2);
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20

    3、继承

    /**
     * Dog extends Animal
     * 此时,Animal 被称为 父类,Dog 被称为 子类
     * 使用继承后,子类会拥有父类所有的方法和属性
     * 使用继承,可以将多个类中共有的代码写在一个父类中
     *    这样值需要写一次就可以让所有子类都同时拥有父类的属性
     *    如果希望在子类中添加一些父类中没有的属性或者方法,可以在子类中直接加
     * 如果在子类中添加了和父类相同的方法,则子类方法会覆盖父类的方法
     *    这种子类覆盖掉父类的形式,称为 方法重写
     */
    
    class Dog extends Animal {
      talk(){
        console.log("汪汪汪");
      }
      run() {
        console.log(`${this.name}在跑`);
      }
    }
    class Cat extends Animal {
      talk(){
        console.log("喵喵喵");
      }
    }
    
    let dog = new Dog("小狗", 6)
    let cat = new Cat("小猫", 8)
    
    console.log(dog);
    dog.talk()
    dog.run()
    console.log(cat);
    cat.talk()
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33

    在这里插入图片描述

    4、super 关键字

    如果子类需要额外的属性,那么就要用到super(子类的构造函数中,调用父类构造函数,处理父类所需的属性)
    否则,不需要用super

    class Animal {
      name: string
      constructor(name: string) {
        this.name = name
      }
      talk() {
        console.log("动物在叫");
      }
    }
    
    class Dog extends Animal {
      age: number
      constructor(name: string, age: number) {
        //如果在子类中写了构造函数,在子类构造函数中必须对父类的构造函数进行调用
        //如果不重写,默认会自动调用父类的构造函数
        super(name)   //调用父类的构造函数,传入父类构造函数所需要的参数
        this.age = age
      }
      talk() {
        //在类的方法中,super 就表示当前类的父类
        // super.talk()  //动物在叫
        console.log("汪汪汪");
      }
    }
    
    let dog = new Dog("小狗", 2)
    console.log(dog);
    dog.talk()
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28

    5、抽象类

    • 以abstract开头的类是抽象类
      抽象类和其他类区别不大,只是不能用来创建对象
      抽象类就是专门用类被继承的类()父类
    • 抽象类中还有抽象方法,也是需要以 abstract 开头,在抽象类中只做声明,没有方法体,需要在子类中定义方法
    /**
     * 以abstract开头的类是抽象类
     *    抽象类和其他类区别不大,只是不能用来创建对象
     *    抽象类就是专门用类被继承的类(父类)
     */
    abstract class Animal {
      name: string
      constructor(name: string) {
        this.name = name
      }
      //定义一个抽象方法
      //抽象方法使用 abstract 开头,没有方法体
      //抽象方法只能定义在抽象类中,子类必须对抽象方法进行重写
      abstract sayHello():void;
    }
    class Cat extends Animal {
      sayHello() {
        console.log("喵喵喵");
      }
    }
    let cat = new Cat("小猫")
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21

    6、接口

    /**
     * 接口用来定义一个类结构,用来定义一个类中应该包含哪些属性和方法
     * 同时接口也可以当成类型声明去使用
     * 它是一种规范
     */
    
    interface myInter {
      name: string,
      age: number
    }
    //如果定义同名的接口,接口数据会合并
    interface myInter {
      gender: string
    }
    
    let obj: myInter = {
      name: "张三",
      age: 18,
      gender: "男"
    }
    
    /**
     * 接口可以在定义类的时候去限制类的结构
     *    接口中所有的属性都不能有实际的值
     *    接口只定义对象的结构,而不考虑实际值
     *        接口中所有的方法都是抽象方法
     */
    interface myInterface {
      name: string,
      sayHello():void
    }
    /**
     * 定义类是,可以使类去实现一个接口
     *    实现接口就是使用类满足接口的要求
     */
    class MyClass implements myInterface {
      name: string
      constructor(name: string) {
        this.name = name
      }
      sayHello() {
        console.log("你好啊");
      }
    }
    let myclass = new MyClass("张三")
    console.log(myclass);
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46

    7、属性的封装

    TS 可以在属性前添加属性的修饰符

    • public 修饰的属性可以在任意位置(父类、子类、实例)访问(修改),是默认值
    • private 私有属性,私有属性只能在类的内部访问(修改)
      • 通过在类中添加方法,是得私有属性可以被外部访问
    • protected 受包含的属性,只能在当前类和当前类的子类中访问(修改)
    /**
     * TS 可以在属性前添加属性的修饰符
     * public 修饰的属性可以在任意位置(父类、子类、实例)访问(修改),是默认值
     * private 私有属性,私有属性只能在类的内部访问(修改)
     *    - 通过在类中添加方法,是得私有属性可以被外部访问
     * protected 受包含的属性,只能在当前类和当前类的子类中访问(修改)
     */
    class Person {
      name: string;
      private _age: number;
      private gender: string;
      constructor(name: string, _age: number, gender: string) {
        this.name = name;
        this._age = _age;
        this.gender = gender;
      }
      getGender() {
        return this.gender
      }
      /**
       * getter 方法分用来读取属性
       * setter 方法用来设置属性
       *    它们被称为属性的存取器
       */
      get age() {
        return this._age
      }
      set age(value: number) {
        //使用 set 可以统一处理设置的值,如 name 属性则可以被任意修改,
        //属性可以任意被修改会将导致对象中的数据变得非常不安全
        if(value > 0) {
          this._age = value
        }
      }
    }
    let per = new Person("张三", 20, "男")
    per.age = 18
    console.log(per.age);
    console.log(per.getGender());
    console.log(per);
    
    // protected 
    class B {
      protected num: number
      constructor(num: number) {
        this.num = num
      }
    }
    class C extends B{
      test() {
        console.log("----"+this.num);
        
      }
    }
    let c = new C(18)
    c.test()
    console.log(c.num);   //受 protected 影响,实例无法访问 num
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57

    8、泛型

    /**
     * 在定义函数或类时,如果遇到类型不明确就可以使用泛型
     */
    function fn<T>(a: T): T {
      return a
    }
    //可以直接调用具有泛型的函数
    fn(1);  //不指定泛型,TS 可以自动对类型进行推断
    fn<number>(1);  //指定泛型
    
    //泛型可以同时指定多个
    function fn2<T, K>(a: T, b: K): T {
      console.log(b);
      return a
    }
    fn2("hello", 12);
    fn2<string, number>("hello", 12);
    
    
    interface inter {
      length: number
    }
    //T extends inter 表示泛型 T 必须是 inter 实现类(子类)
    function fn3<T extends inter>(a: T): number {
      return a.length
    }
    class MyClass<T> {
      name: T;
      constructor(name: T) {
        this.name = name
      }
    }
    let mc = new MyClass<string>("张三")
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
  • 相关阅读:
    硬盘如何正确使用
    Altium Designer 19.1.18 - 画多边形铜皮挖空时,针对光标胡乱捕获的解决方法
    Linux操作系统之基础IO
    【OpenCV-Python】教程:3-7 Canny边缘检测
    Mac电脑专业raw图像处理 DxO PhotoLab 7中文最新 for mac
    11.netty入站与出站(处理器链调用机制)
    Go 的三种指针
    通信达交易接口常量定义
    SpringMVC:绝对地址、相对地址(动力)
    Springboot 整合 JWT + Redis 实现双Token 校验Demo(简单实现)
  • 原文地址:https://blog.csdn.net/qq_41604686/article/details/134312521