TypeScript

273 阅读7分钟

1、环境搭建

  1. 安装Node.js
  2. 使用npm包管理器全局安装typescript

    npm i -g typescript

  3. 创建ts文件

    % touch helloTS.ts

  4. ts文件不能直接被浏览器识别,因此先使用tsc命令对ts文件进行编译

    % tsc helloTS.ts 执行完毕文件夹中生成同名js文件

2、TS类型

2.1、函数返回

: number 表示返回值类型
function sum(a: number, b: any): number {
    return a + b;
}

2.2、number、string、boolean、字面量

let c:number;
let d:string;

// 赋值字面量,联合类型,只允许输入后边规定的
let e: boolean | string;
e = true;
e = '10';
e = 5       // 报错:不能将类型"5"分配给类型"string | boolean"

// 只可以赋值'male'与'female'
let f: 'male' | 'female';
f = 'male';
f = 10;     // 报错:不能将类型"10"分配给类型"male | female"

2.3、any、unknown

any类型变量在给其他变量赋值时会将其也变成像any一样什么类型都能接收,(因此尽量不用);unknown只有经过类型判断或者类型断言才能给其他变量赋值

// unknown是类型安全的any,可以赋值为任意类型
// 给其他变量赋值时any会改变被赋值变量类型,但unknown会报错,除非经过类型检测或类型断言
let g: unknown;
g = true;
if(typeof g === 'boolean' || typeof g === 'string'){
    e = g;
}
// 类型断言
e = g as boolean;

2.4、void、never

void返回undefined,never直接拒绝返回

function mixNum(a: string, b:boolean): never {
    throw new Error();
}

2.5、object

2.5.1、对象

索引类型约定了属性的key与value类型具体属性名与值在调用时随意填写

// 索引类型,规定属性的key为string类型,值为unknown类型(有一个属性规定为name,值为string类型了)
let h: {name?: string, [propName: string]: unknown};
h = {name: 'lz',age: 18, isMale: true};

注意:如果将unknown换成number,则前边定义的name?:string类型不符合报错;若换成string,则因name为可选类型有可能为undefined,所以也不符合

2.5.2、函数对象
// 函数的结构: (形参:类型, 形参:类型...) => 返回值
let i: (a: number, b: number) => number;
i = function(n1: number, n2: number): number {
    return 10;
}
2.5.3、& 与 |
// &:需要同时赋值
let m: {name: string} & {age: number};
m = {
    name: '猪八戒',
    age: 20
}

2.6、Array、元组

元组固定长度的数组

// 普通数组
let j: Array<string>;

// 元组
let k: [string, number, boolean];
k = ['hello', 18, true];

2.7、枚举

enum Gender {
    Male = 0,
    Female = 1
}

// 创建对象
let l: {name: string, gender: Gender};
l = {
    name: '孙悟空',
    gender: Gender.Male
}

console.log(l.gender === Gender.Male);

2.8、别名

// 将取值范围1、2、3、4、5的类型取别名为myType
type myType = 1 | 2 | 3 | 4 | 5 ;
let n: myType;
n = 6;        // 取值6时报错

3、编译选项

3.1、自动监测生成js文件

  • 首先生成tsconfig.json文件

    % tsc --init

  • 执行监测所有ts文件

    % tsc -w

3.2、tsconfig.json

  • includeexclude:指定哪些ts文件需要被编译和忽略
  • compilerOptions:比较重要的为target(ES版本)module(模块化规范)outDir(编译后文件路径)removeComments(移除注释)noEmitOnError报错不生成编译文件)
  • **:表示 任意目录、*:表示 任意文件
{
    // 用来指定哪些ts文件需要被编译
    "include": ["./**/*"],
    // 指定哪些ts文件不需要编译,有默认值,偶尔使用
    "exclude": ["./ts/02_basis.ts"],
    
    "compilerOptions": {
        // 指定ts被编译为的ES版本
        "target": "es2016",
        // 指定要使用的模块化规范
        "module": "commonjs",
        // 指定项目中要使用的库,默认不用使用
        "lib": ["ES6", "DOM"],
        // 指定编译后文件所在目录
        "outDir": "./dist",
        // 将代码合并为一个文件,module项只能是amd或者system
        "outFile": "./dist/ts/01_helloTS.js",

        /* JavaScript Support */
        // 是否对JS文件进行编译,默认为false
        "allowJs": false,
        // 检查JS代码是否符合语法规范 /* Allow JavaScript files to be a part of your program. Use the 'checkJS' option to get errors from these files. */
        "checkJs": false,
        // 是否移除注释 /* Specify an output folder for all emitted files. */
        "removeComments": true,
        // 当有错误时不生成编译文件
        "noEmitOnError": true,
        
        /* Interop Constraints */
        "esModuleInterop": true,
        "forceConsistentCasingInFileNames": true,
        
        /* Type Checking */
        // 严格检查总开关,这个开启后剩余的严格检查都可以注释掉
        "strict": true,
        // 不允许隐式的any类型
        "noImplicitAny": true,
        // 严格检查空值
        "strictNullChecks": true,
        // 不允许不明确类型的this
        "noImplicitThis": true,
        // 指定编译后的文件是否使用严格模式
        "alwaysStrict": true,
        
        /* Completeness */
        "skipLibCheck": true
    }
}

4、webpack打包

4.1、配置

  1. 生成package.json,里边是包的配置信息

    % npm init -y

  2. 通过npm下载4个webpage所需的包

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

  3. 创建并修改webpack.config.js文件
    // 引入一个包
    const path = require('path');
    
    // webpage中所有的配置信息都要写在module.exports中
    module.exports = {
        // 指定入口文件
        entry : "./src/index.ts",
    
        // 指定打包文件所在目录
        output : {
            // 指定打包文件路径
            path: path.resolve(__dirname, 'dist'),
            // 打包后文件的名字
            filename: "bundle.js"
        },  
    
        // webpack4.0对语法要求更加严格,webpack 时必须在 "开发或者生产" 中选择一种模式,否则报错
        mode: 'development',
    
        module : {
            // 指定要加载的规则
            rules: [
                {
                    // 指定规则生效的文件(正则匹配以.ts结尾的文件)
                    test: /\.ts$/,
                    // 要使用的loader
                    use: 'ts-loader',
                    // 排除node_modules文件夹
                    exclude: /node_modules/
                }
            ]
        }
    }
    
  4. 修改 tsconfig.json 文件,参考3.2
  5. 修改 package.json,增加webpack方法
    {
        "name": "tstest",
        "version": "1.0.0",
        "description": "",
        "main": "index.js",
        "scripts": {
            "test": "echo \"Error: no test specified\" && exit 1",
            // 新增build方法
            "build": "webpack"
        },
        "keywords": [],
        "author": "",
        "license": "ISC",
        
        // npm安装的4个包
        "devDependencies": {
            "ts-loader": "^9.4.1",
            "typescript": "^4.8.4",
            "webpack": "^5.75.0",
            "webpack-cli": "^4.10.0"
        }
    }
    
  6. 执行build方法

    % npm run build

4.2、集成实用插件

4.2.1、自动生成html插件

自动生成html文件并将编译后的js文件引入

  1. 安装 html-webpack-plugin 插件

    % npm i -D html-webpack-plugin

  2. 修改 webpack.config.js 文件,先引入插件,再调用
    // 引入一个包
    const path = require('path');
    // 引入HTML插件
    const HTMLWebpackPlugin = require('html-webpack-plugin');
    module.exports = {
        entry : "./src/index.ts",
        ...
        // 配置webpack插件
        plugins : [
            new HTMLWebpackPlugin({
                title:"自定义html名称"
                // 以自定义的一个html文件为模板创建新html文件
                template: "./xx/template.html"
            }),
        ]
    }
    
    image.png
4.2.2、自动删除dict目录
  1. 安装 clean-webpack-plugin 插件

    % npm i -D clean-webpack-plugin

  2. 修改 webpack.config.js 文件,先引入插件,再调用
    // 引入一个包
    const path = require('path');
    // 引入HTML插件
    const HTMLWebpackPlugin = require('html-webpack-plugin');
    // 不是默认暴露的,加{}
    const { CleanWebpackPlugin } = require('clean-webpack-plugin')
    module.exports = {
        entry : "./src/index.ts",
        ...
        // 配置webpack插件
        plugins : [
            new CleanWebpackPlugin(),
            new HTMLWebpackPlugin({
                title:"自定义html名称"
                // 以自定义的一个html文件为模板创建新html文件
                template: "./xx/template.html"
            }),
        ]
    }
    

4.3、设置引用模块

  1. 在另一个TS文件中写入可以被其他文件引入的模块 m.ts
    export const hi = '你好';
    
  2. 在index.ts文件中引入
    import { hi } from "./m";
    
  3. 此时运行会报Module not found的错 image.png
  4. webpack.config.js 文件中增加resolve配置
    module.exports = {
        ...
    
        // 用来设置引用模块
        resolve: {
            extensions: ['.ts', '.js']
        }
    }
    
  5. 再次运行即可正常

4.4、babel

兼容旧版本的代码,将新语法按照功能等价,转换成旧语法

  1. 安装babel插件

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

    // babel核心库
    @babel/core
    // babel设置兼容不同浏览器环境
    @babel/preset-env
    // 将babel与webpack进行结合
    babel-loader
    // 模拟JS运行环境,让老版本浏览器体验新版本的技术
    core-js
    
  2. 修改 webpack配置

    // 引入一个包
    const path = require('path');
    // 引入HTML插件
    const HTMLWebpackPlugin = require('html-webpack-plugin');
    const { CleanWebpackPlugin } = require('clean-webpack-plugin')
    
    // webpage中所有的配置信息都要写在module.exports中
    module.exports = {
        // 指定入口文件
        entry : "./src/index.ts",
    
        output : {
            // 指定打包文件路径
            path: path.resolve(__dirname, 'dist'),
            // 打包后文件的名字
            filename: "bundle.js",
    
            // 修改1: 告诉webpack不使用箭头函数
            environment: {
                arrowFunction : false
            }
        },  
    
        mode: 'development',
        module : {
            // 指定要加载的规则
            rules: [
                {
                    // 指定规则生效的文件(正则匹配以.ts结尾的文件)
                    test: /\.ts$/,
                    // 修改2: 要使用的loader
                    use: [
                        {
                            // 指定加载器
                            loader: "babel-loader",
                            // 设置babel
                            options: {
                                // 设置预定义的环境
                                presets: [
                                    [
                                        // 指定环境的插件
                                        "@babel/preset-env",
                                        // 配置信息
                                        {
                                            // 要兼容的目标浏览器,谷歌浏览器 --> 帮助 --> 关于 可查看
                                            targets: {
                                                "chrome" : "107",
                                                "ie" : "11"
                                            },
                                            // 指定corejs版本,与package.json中corejs版本号有关
                                            "corejs":"3",
                                            // 使用corejs的方式, usage为按需加载
                                            "useBuiltIns":"usage"
                                        }
                                    ]
                                ]
                            }
                        },
                        // 两个loader,写到后边的先执行,先将ts代码转为js,再由新版js转为旧版js
                        'ts-loader'
                    ],
                    // 排除node_modules文件夹
                    exclude: /node_modules/
                }
            ]
        },
    
        // 配置webpack插件
        plugins : [
            new CleanWebpackPlugin(),
            new HTMLWebpackPlugin({
                // title:"自定义html名称"
                // template: "./dist/index.html"
            }),
        ],
    
        // 用来设置引用模块
        resolve: {
            extensions: ['.ts', '.js']
        }
    }
    

    修改1:output 内增加 environment: { arrowFunction : false },输出文件不使用箭头函数

    修改2:module --> rules --> use 内改为使用的 loader数组,且写到后边的先执行,先将ts代码转为js,再由新版js转为旧版js;其中可以配置环境插件的兼容浏览器、corejs版本

4.5、less

  1. 安装 less 插件,以及postcss兼容插件(类似babel)

    npm i -D less less-loader style-loader css-loader postcss postcss-loader postcss-preset-env

  2. 修改webpack.config.js文件
    module.exports = {
        ......
    
        module : {
            // 指定要加载的规则
            rules: [
                {
                    ......
                }
                // 设置less文件的处理
                {
                    test: /\.less$/,
                    use: [
                        "style-loader",
                        "css-loader",
                        // 引入postcss,做兼容
                        {
                            // 指定加载器
                            loader: "postcss-loader",
                            options: {
                                postcssOptions: {
                                    // 此处写成plugins会报错
                                    plugin: [
                                        // 指定环境的插件
                                        "postcss-preset-env",
                                        {
                                            // 2个最新版本的浏览器
                                            browser: 'last 2 versions'
                                        }
                                    ]
                                }
                            }
                        },
                        "less-loader"
                    ]
                }
            ]
        },
        ......
    }
    
  3. 创建 index.less 文件
    body {
        background-color: #bbb;
        display: flex;
    }
    
  4. index.ts 文件中引入
    import './style/index.less';
    

5、类、对象

  • 类可以定义属性方法构造函数:constructor,属性分为 实例属性用new出的对象调用)与 类属性类直接调用),实例方法与类方法与之类似

  • 类被继承,如果构造函数 constructor 不重写则系统默认调用super构造函数,如果重写则必须对构造函数进行实现:constructor()改为super()

  • 实例方法中,this表示当前被调用的这个实例(可能new了一堆的实例);但在 类方法中this表示当前类箭头函数中还可能会表示成Windows

  • 使用关键字extends进行继承

5.1、抽象类、抽象方法

  • 使用关键字abstract
  • 抽象类:专门用来被继承的,被禁止用于创建对象
  • 抽象方法:专门用来被继承的,被禁止添加具体实现
// 抽象类,专门用来被继承,被禁止用于创建对象
abstract class Animal {
    name: string;
    // 实例属性
    age: number;
    // 类属性、静态属性,通过类直接调用,设置了只读属性
    static readonly country: string = 'China';

    // 构造函数
    constructor(name: string, age: number) {
        // 在实例方法中,this表示当前被使用的实例;类方法中可不是
        this.name = name;
        this.age = age;
    }

    // 实例方法
    run() {
        console.log(`${this.name} is run`); 
    }

    // 类方法
    static bark() {
        console.log(`${this.country}在叫`);
    }
    // 抽象方法
    abstract play():void;
}

// 继承抽象类
class Cat extends Animal {
    sex: string;
    // 如果重写了constructor方法就必须调用父类的构造函数
    constructor(name: string, age: number, sex: string) {
        super(name, age);
        this.sex = sex;
    }

    run(): void {
        super.run();
        console.log('jump');
    }

    play(): void {
        console.log('play');
    }
}

const cat = new Cat('小白', 18, '男');
console.log(Cat.country , cat.age);
Cat.bark();
cat.run();

5.2、接口:interface

  • 使用关键字interface定义

  • 使用关键字implements实现接口内容

  • 用于定义一个标准,让某个类去符合这个标准

  • 接口与抽象类很相似,都是通过 被其他类继承 / 实现来完成功能,不同是 抽象类中可以有析构函数、抽象的和非抽象的内容,而接口中只定义对象结构,属性不能有值,方法不能有实现;所有方法都是抽象方法

// 描述一个对象的类型,与接口结构相似
type myType = {
    name: string,
    age: number
};

// 接口用来定义一个类结构,同时也能当做类型声明去使用
// 接口中只定义对象结构,属性不能有值,方法不能有实现;所有方法都是抽象方法
interface myInterface {
    name: string;
    age: number;
}

// 同名接口内容会进行合并扩展
interface myInterface {
    sex: string;
    run(): void;
}

// 实现接口
class MyClass implements myInterface {
    name: string;
    age: number;
    sex: string;

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

    run():void {
        console.log(`${this.name} is running`);
    }
}

6、属性

权限关键字:

  • public任意位置 访问修改默认值
  • protected:只能在 当前类及其子类 中访问
  • private:只能在 当前类内部 进行访问修改;通过类中添加方法使私有属性被外界访问
class Person {
    // 任意位置访问修改默认值
    public _name:string;
    // 只能在当前类内部进行访问修改;通过类中添加方法使私有属性被外界访问
    private _age:number;
    // 只能在当前类及其子类中访问
    protected _sex:string;

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

    getAge() {
        return this._age;
    }
    // 自定义set方法修改私有属性
    setAge(newValue: number) {
        if (newValue >= 0 ) {
            this._age = newValue;
        }
    }
}

const per = new Person('lz', 18);
per.setAge(20);

7、泛型

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

function fn<T, K>(a: T, b:K): T{
    return a;
}
// 推断泛型
let result = fn('lz', 18);
// 指定泛型
let result2 = fn<string, number>('lz', 18);


// 约束泛型范围  
interface Inter {
    length: number;
}
// 规定泛型K必须是接口Inter的实现类,用extends
function fn2<T, K extends Inter>(a: T, b:K): number{
    return b.length;
}

class AA implements Inter {
    length: number = 18;
}
const aa = new AA(); 

// 此时传的就不能是number而是实现了Infer接口的类
let result3 = fn<string, Inter>('lz', aa);
  • 泛型与Any有些类似,但同时用Any作为形参类型与返回值类型,这两者可能是不同类型,但用泛型则肯定是 同一类型 只是不知道具体是哪种类型

  • 调用时可以指定泛型类型

  • 可以通过继承interface或者类来约束泛型范围,但 传入的实参就需要是实现了Infer接口的类了(注:这里用 interface 进行约束也要用 extends 而非 implements