TypeScript笔记

72 阅读7分钟

1.TS简介

01.TypeScript是什么?

TypeScript是JavaScript类型的超集,可以编译纯JavaScript。它可以在任何浏览器、计算机和操作系统上运行,并且是开源的。

注意:ts不能被js解析器直接执行。ts需编译成js。

02.ts增加了什么?

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

2.TypeScript开发环境搭建

01.安装node.js

02.使用npm全局安装TypeScript

  • 进入命令行

  • 输入npm install -g typescript

03.创建一个ts文件

04.使用tsc对文件编译

  • 进入命令行
  • 进入ts文件所在目录
  • 执行命令:tsc xxx.ts

05.ts编译同输出编译结果(替代05方法)(推荐)

3.TS的基本类型

01.类型声明

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

  • 类型声明可以制定ts中变量(参数、形参)的类型

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

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

  • 语法:

    • let 变量:类型;
      let 变量:类型=值;
      
      function fn(参数:类型,参数:类型):类型{
          ...
      }
      

02.自动类型判断

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

03.类型

类型例子描述
number1,-2,2.5任意浮点数
string"hello","123"任意字符串
booleantrue,false布尔值
字面量其本身限制字面量的值就是该字面量的值
any*任意类型
unknown*类型安全的any
void空值没有值
never没有值不能是任何值
object{name:'孙悟空'}js对象
array[1,2,3]js数组
tuple[4,5]ts新类型,固定长度数组
enumenum {A,B}ts新类型,枚举
  • number

    // 声明一个变量a,同时指定它的类型为number
    let a: number;
    // 之后a的值只能是数字
    a = 20;
    
  • string

    // 声明完直接赋值
    let str: string = "hello";
    
  • boolean

    let c: boolean = false;
    c = true;
    
  • function

    // 函数类型
    let d2: (a: number, b: number) => number;
    d2 = function (n1: number, n2: number): number {
      return n1 + n2;
    };
    
    function sum(a: number, b: number): number {
      return a + b;
    }
    let res = sum(1, 2);// =>3
    
  • 字面量

    也可以使用字面是去指定变量的类型,通过字面是可以确定变量的取值范围;

    let color: 'red' | 'blue' | 'black' ;
    let num:1|2|3|4|5;
    
  • any

    let d:any=4;
    d = 'hello';
    d = true;
    
  • unknown

    // unknown 表示未知类型的值
    let e: unknown;
    e = true;
    e = 1;
    e = "str";
    
    // 不能将类型“unknown”分配给类型“string”。
    if (typeof e == "string") {
      s = e;
    }
    
  • viod

    // void 表示空 ,以函数为例,表示无返回值
    function f(): void {}
    
    let unusable: void = undefined;
    
  • never

    // never 表示永远不会返回结果
    function f2(): never {
      throw new Error("error");
    }
    
  • object

    // object 表示一个js对象
    let obj: object;
    obj = {};
    obj = function () {};
    
    // {}用来指定对象中可以包含那些属性
    // 在属性名后面加上?,表示属性是可选的
    let obj2: { name: string; age?: number };
    obj2 = { name: "孙悟空", age: 18 };
    
    // [propName:string]:any 表示任意类型的属性
    let o3: { name: string; [propName: string]: any };
    o3 = { name: "lisi", a: 14 };
    
  • array

    // 数组类型
    // string[] 表示字符串数组
    let arr2: string[];
    arr2 = ["1", "2", "3"];
    
    // number[] 表示数值数组
    let arr3: number[];
    arr3 = [1, 2, 3];
    
    let arr4: Array<number>;
    arr4 = [1, 2, 3];
    
  • tuple

    // 元祖类型 固定长度的数组
    let tup: [string, string];
    tup = ["1", "2"];
    
  • enum

    // 枚举类型
    enum Gender {
      Male,
      Female,
    }
    let i: { name: string; gender: Gender };
    i = {
      name: "lisi",
      gender: Gender.Male,
    };
    console.log(i.gender === Gender.Male);
    
    // & 表示同时
    let and: { name: string } & { age: number };
    and = { name: "lisi", age: 12 };
    
    // 类型的别名
    type myType = 1 | 2 | 3;
    let t: myType;
    t = 1;
    t = 3;
    

04.类型断言

有些情况下,变量的类型对于我们来说是很明确,但是TS编译器却并不清楚, 此时,可以通过类型断言来告诉编译器变量的类型,断言有两种形式:

  • 第一种

    let somevalue: unknown = "this is a string":
    let strLength: number = (somevalue as string) . length;
    
  • 第二种

    let somevalue: unknown = "this is a string";
    let strLength: number = (<string> someValue).length; 
    
// 类型断言 告诉解析器变量的实际类型
s = e as string;
s = <string>e;

4.编译选项

01.自动编译文件

编译文件时,使用-W指令后, TS编译器会自动监视文件的变化并在文件发生变化时对文件进行重新编译。

tsc xxx.ts -w

02.自动编译整个项目

如果直接使用tsC指令,则可以自动将当前项目下的所有ts文件编译为js文件。

但是能直接使用tsc命令的前提时,要先在项目根目录下创建一 个ts的配置文件tsconfigjson

tsconfig,json是一 个SON文件, 添加配置文件后。只需只需tsc命令即可完成对整个项目的编译

03.配置选项:

  • include

    "include":["src/**/*""tests/**/*"]
    

    上述示例中,所有src目录和tests目录下的文件都会被编译

  • exclude

    ■定义需要排除在外的目录

    ■默认值: ["node. _modules", "bower, components", "jspm packages"]

    ■示例:

    "exclude": ["./src/he11o/**/*"]
    

    ■上述示例中,src 下hello目录下的文件都不会被编译

  • extends

    ■定义被继承的配置文件

    ■示例:

    {
       "files": [
           "sys.ts",
           "types.ts",
           "scanner .ts",
           "parser.ts"
    	] 
    }
    

    ■列表中的文件都会被TS编译器所编译

demo:

{
  // 用来指定那些ts文件需要被编译
  //   ** 任意目录
  // * 任意文件
  "include": ["./src/**/*"],
  //   不需要被编译的文件目录
  "exclude": ["./src/hello/**/*"],
  //   compilerOptions 编译器的选项
  "compilerOptions": {
    //   target 用来指定ts被编译为的es版本
    "target": "es5",
    // module 指定要使用的模块化规范
    "module": "system",
    // lib 指定项目中要使用的库
    "lib": ["dom", "ES6"],
    // outDir 指定编译后文件所在目录
    "outDir": "./dist",

    // 将代码合并为一个文件
    // 设置outfile后,所有的全局作用域中的代码会合并到同一个文件中
    "outFile": "./dist/app.js",

    // 是否对js文件进行编译
    "allowJs": true,
    // 是否检查 js代码是否符合规范
    "checkJs": true,

    // 是否移除注释
    "removeComments": true,

    // 是否不生成编译后的文件
    "noEmit": false,
    // 是否 有错误时不生成编译文件
    "noEmitOnError": false,

    // 所有严格检查的总开关
    "strict": true,
    // 设置编译后的文件是否使用严格模式
    "alwaysStrict": true,

    // 是否 不允许隐式的any类型
    "noImplicitAny": true,

    // 不允许不明确类型的this
    "noImplicitThis": true,

    // 是否 严格检查空值
    "strictNullChecks": true
  }
}

5.webpack打包ts代码

  • 基本配置流程:

    npm初始化

    npm init -y
    

    安装webpack、 webpack-cli、typescript、ts-loader

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

npm i -D @babel/core --save npm i -D @babel/preset-env babel-loader core-js --save


webpack.config.js

```js
// 引入一个包
const path = require("path");

// webpack中的所有配置信息都应该写在module.exports中
module.exports = {
  mode: "none",
  // 使得入口文件
  entry: "./src/index.ts",

  // webpack 如何输出结果的相关选项
  output: {
    //   指定打包文件所在目录
    path: path.resolve(__dirname, "dist"),
    // 打包后文件的名字
    filename: "bundle.js",
  },

  //   webpack打包时要使用的模块
  module: {
    //   指定要加载的规则
    rules: [
      {
        //   test指定的是规则生效的文件
        test: /\.ts$/,
        // 要使用的loader
        use: "ts-loader",
        // 要排除的文件
        exclude: /node_modules/,
    },
    ],
},
};

tsconfig.json

{
    "compilerOptions": {
        "module": "es6",
        "target": "ES6",
      "strict": true,
        "noEmit": false
  }
}

package.json

{
  "name": "pro",
  "version": "1.0.0",
  "description": "",
  "main": "index.js",
  "scripts": {
    "test": "echo \"Error: no test specified\" && exit 1",
    "build": "webpack",
    "start": "webpack serve --open"
  },
  "keywords": [],
  "author": "",
  "license": "ISC",
  "devDependencies": {
    "ts-loader": "^9.2.6",
  "typescript": "^4.4.4",
    "webpack": "^5.61.0",
    "webpack-cli": "^4.9.1"
  }
}

执行 npm run build

6.面向对象

简单来说,所有的操作通过对象来完成。

如:

  • 操作浏览器使用window对象
  • 操作网页使用document对象
  • 操作控制台使用console对象

01.类(class)

传统的JavaScript程序使用函数和基于原型的继承来创建可重用的组件,但对于熟悉使用面向对象方式的程序员来讲就有些棘手,因为他们用的是基于类的继承并且对象是由类构建出来的。

02.类的简介

class Person {
  // 只读
  readonly name: string = "孙悟空";
  age: number = 20; // 实例属性
  //   静态属性
  static n: string = "lisi";

  //   定义方法
  sayHello() {
    console.log("hello world");
  }
}

let per = new Person(); // 实例方法
console.log(per);
// console.log(per.name, per.age);
// per.name = "zhangsan";
// console.log(per.name, per.age);
// console.log(Person.n);
per.sayHello();
// Person.sayHello()

03.构造函数

class Dog {
  name: string;
  age: number;
  constructor(name: string, age: number) {
    this.name = name;
    this.age = age;
    // console.log(this);
  }

  bark() {
    alert("汪汪");
    console.log(this.name);
    
  }
}

let dog = new Dog("小盒", 4);
let dog2 = new Dog("大黑", 5);
dog.bark()
dog2.bark()
// let dog2 = new Dog();
// let dog3 = new Dog();
// console.log(dog);

04.继承简介

(() => {
  // 定义一个anymal类
  class Animal {
    name: string;
    age: number;
    constructor(name: string, age: number) {
      this.name = name;
      this.age = age;
    }

    sayHello() {
      console.log("动物再叫");
    }
  }

  class Dog extends Animal {
    run() {
      console.log(`${this.name}--再叫`);
    }
  }

  //   定义一个猫的类
  class Cat extends Animal {
    sayHello() {
      console.log(`${this.name}--再叫`);
    }
  }

  let dog = new Dog("旺财", 5);
  let cat = new Cat("咪咪", 2);
  console.log("dog", dog);
  dog.sayHello();
  dog.run();
  console.log("cat", cat);
  cat.sayHello();
})();

05.super关键字

(() => {
  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(): void {
      super.sayHello();
    }
  }

  let dog = new Dog("小白", 5);
  console.log(dog);

  dog.sayHello();
})();

06.抽象类

(() => {
  /*
   * 以abstract开头的类时抽象类,
  特点:
        抽象类和其他类区别不大,只是不能用来创建对象
        抽象类就是专门用来被继承的类
        抽象类中可以添加抽象方法
   */
  abstract class Animal {
    name: string;

    constructor(name: string) {
      this.name = name;
    }

    // 定义一个抽象方法
    // 抽象方法使用abstract开头,没有方法体
    // 抽象方法只能定义在抽象类中,子类需对抽象方法重写
    abstract sayHello(): void;
  }

  class Dog extends Animal {
    age: number;
    constructor(name: string, age: number) {
      super(name);
      this.age = age;
    }

    sayHello() {
      console.log("汪汪");
    }
  }

  let dog = new Dog("小白", 5);

  dog.sayHello();
})();

7.接口

TypeScript的核心原则之一是对值所具有的结构进行类型检查。 它有时被称做“鸭式辨型法”或“结构性子类型化”。 在TypeScript里,接口的作用就是为这些类型命名和为你的代码或第三方代码定义契约。

(() => {
  // 描述一个对象的类型
  type myType = {
    name: string;
    age: number;
  };

  /* 
    接口用来定义一个类结构,用来定义一个类中应该包含那些属性和方法
  */
  interface myInterface {
    name: string;
    age?: number;
    // readonly:只读属性
    readonly summary:string;
  }
  
  
interface Cache {
  // 动态成员
  [prop: string]: string
}

const cache: Cache = {}

cache.foo = 'value1'
cache.bar = 'value2'

  interface myInterface {
    gender: string;
  }
  const obj: myInterface = {
    name: "李四",
    age: 20,
    gender: "男",
  };
  console.log(obj);

  /* 
    接口可以在定义类的时候去限制类的结构
        接口中的所有属性都不能有实际的值
        接口只定以对象的结构,而不考虑实际值
        在接口中所有的方法都是抽象方法
  */

  interface myInter {
    name: string;
    sayHello(): void;
  }

  /* 
    定义类时,可以使类去实现一个接口,
        实现接口就是使类满足接口的要求
  */
  class MyClass implements myInter {
    name: string;
    constructor(name: string) {
      this.name = name;
    }
    sayHello(): void {}
  }
})();

属性的封装

(() => {
  class Person {
    //   TS可以在属性前添加属性的修饰符

    /* 
        public 修饰的属性可以在任意位置访问(修改)默认值
        private 私有属性,只能在类内部进行访问(修改)
        protected 受保护的属性,可以再父类和继承的子类中访问
    */
    public name: string;
    private _age: number;

    constructor(name: string, age: number) {
      this.name = name;
      this._age = age;
    }

    // 定义方法,获取age
    getAge() {
      return this.age;
    }

    // 定义方法,设置age
    setAge(value: number) {
      this._age = value;
    }

    get age(): number {
      return this._age;
    }

    set age(v: number) {
      console.log("v", v);
      if (v >= 0) {
        this._age = v;
      }
    }
  }

  const per = new Person("小白", 2);
  per.name = "zhangsan";
  per.age = -123;
  console.log(per);
})(); // 立即执行函数

8.泛型

// 在定义函数或类时,如遇到类型不明确时就可以使用泛型

function fn<T>(a: T): T {
  return a;
}

// 可以直接调用具有泛型的函数
let res = fn(10); // 不指定泛型,ts可以自动对类型进行推断
let res2 = fn<string>("hello"); // 指定泛型

// 泛型可以同时指定多个
function fn2<T, K>(a: T, b: K): T {
  console.log(b);
  return a;
}
fn2<string, number>("hello", 123);

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;
  }
}

const mc = new MyClass<string>("hello");

9.项目实践

01.架构搭建

less配置

npm i -D less less-loader css-loader style-loader

css兼容浏览器配置

npm i -D postcss postcss-loader postcss-preset-env

webpack配置

// 引入一个包
const path = require("path");
// 引入html插件
const HtmlWebpackPlugin = require("html-webpack-plugin");
const { CleanWebpackPlugin } = require("clean-webpack-plugin");

// webpack中的所有配置信息都应该写在module.exports中
module.exports = {
  mode: "none",
  // 使得入口文件
  entry: "./src/index.ts",

  // webpack 如何输出结果的相关选项
  output: {
    //   指定打包文件所在目录
    path: path.resolve(__dirname, "dist"),
    // 打包后文件的名字
    filename: "bundle.js",
    // 告诉webpack不使用箭头
    environment: {
      arrowFunction: false,
      const: false,
    },
  },

  //   webpack打包时要使用的模块
  module: {
    //   指定要加载的规则
    rules: [
      {
        //   test指定的是规则生效的文件
        test: /\.ts$/,
        // 要使用的loader
        use: [
          // 配置babel
          {
            // 指定加载器
            loader: "babel-loader",
            // 设置babel
            /* options: {
              // 设置预定义的环境
              presets: [
                // 指定环境的插件
                "@babel/preset-env",
                // 配置信息
                {
                  // 要兼容的目标浏览器
                  targets: {
                    chrome: "88",
                    ie: "11",
                  },
                  // 指定corejs的版本
                  corejs: "3",
                  // 使用corejs的方式 ‘usage 表示按需加载
                  useBuiltIns: "usage",
                },
              ],
            }, */
          },
          "ts-loader",
        ],
        // 要排除的文件
        exclude: /node_modules/,
      },

      // 配置less文件的处理
      {
        test: /\.less$/,
        // 要使用的loader
        use: [
          "style-loader",
          "css-loader",
          // 引入postcss
          {
            loader: "postcss-loader",
            options: {
              postcssOptions: {
                plugins: [
                  [
                    "postcss-preset-env",
                    {
                      browsers: "last 2 versions",
                    },
                  ],
                ],
              },
            },
          },
          "less-loader",
        ],
      },
    ],
  },

  //   配置webpack插件
  plugins: [
    new CleanWebpackPlugin(),
    new HtmlWebpackPlugin({
      // title:'webpack'
      template: "./src/index.html",
    }),
  ],

  //   用来设置引用模块
  resolve: {
    extensions: [".ts", ".js"],
  },
};