TypeScript笔记

第一章 TypeScript基础

1.TypeScript简介

TypeScript是以JavaScript为基础构建的语言(扩展了JavaScript并添加了类型),可以在任何支持JavaScript的平台中执行。

特点:

  • TS不能直接被JS解析器直接执行,需要将TS编译成JS才能执行。
  • TS完全兼容JS,TS是JS的超集;
  • TS为静态类型,具有静态类型检查,自动的类型判断机制。

2.TS编译成JS

tsc xxx.ts
tsc -out 文件夹/xxx.js xxx.ts

但是发现在vscode中输出的js文件和当前ts产生了连接,ts中可能报语法错误 无法重新声明块范围变量“a”。 对于以上错误,可以w外层添加{}
之后发现,对于ts的编译配置文件tsconfig.json,只要添加了里面只写个大括号,之后也不会报错了。

3.TS的类型声明

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

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

自动的类型判断机制:变量声明和赋值同时进行时TS会自动判断变量的类型,可以省略类型声明。

语法

let 变量: 类型;
let 变量: 类型 = 值;
//声明完变量直接赋值,可以省略类型声明:
let 变量 = 值;
function fn(参数: 类型,参数: 类型): 类型{
}

注意:
①使用 | 来连接多个类型(联合类型)
let b: 'male'|'female';
let c: string | boolean;

使用 & 来连接多个类型(表示同时)
let j:string & number;既是string又是number,这个基本用不上,根本不存在。
let j:{name: string} & {age: number};用来连接两个对象,必须同时满足

`可以给类型设置别名:`
type myType = 1|2|3|4;
let j: myType;


②声明变量如果不指明类型,ts解析器会自动判断变量的类型为any(隐式的any)。
any类型的变量可以被赋值任意类型,也可以赋值给其他类型的变量。
let d;


③unknownany不一样,不能赋值给其他类型的变量,ts解析器会报错
(被包含判断条件,不会解析时报错)。
let e: unknown = 2;
if(typeof e === "string"){//ts解析器很智能,写!=就会报错
    s = e;
}
或者用 **类型断言** ,用来告诉解析器变量的实际类型。
s = e as string;
s = <string>e;


④函数不设置返回值,返回类型为void
不声明函数返回类型时,根据函数返回值的情况,自动类型判断。


④指定对象类型,结构必须一模一样,?是属性可选。
let f: {name: string, age?: number};
let f: {name: string, [propName: string]:any}


⑤用箭头函数来设置函数的结构类型声明
let g: (a:number,b: number)=>number;


⑥数组结构类型
let h: string[];
let h: Array<number>;


⑦元组
let i: [string,number];


⑧枚举
enum Gender{
    Male = 0,
    Female = 1
}

类型

类型 例子 描述
number 1,-33 任意数字
string 'hi' 任意字符串
boolean true,false 布尔值true或false
字面量 其本身,例如let a:10;此时a的类型就是10。 限制该变量的值就是字面量的值
any * 任意类型,相当于ts中对该变量关闭了ts的类型检查。可以被赋值或赋值给任意类型的变量,且不会报错。
unknown * 类型安全的any赋值给其他类型的变量,会报错
void 空值(undefined) 没有值或(undefined)
never 没有值 不能是任何值,函数永远执行或者抛出错误。
object {name:'孙悟空'} 任意的js对象
array [1,2,3] 任意js数组
tuple [4,5] 元素,ts新增类型,固定长度数组
enum enum(A,B) 枚举,TS中的新增类型

4.编译选项(自动化+配置ts)

-w监视文件变化,当文件发生变化时,自动对文件进行重新编译。

tsc xxx.ts -w

将所有文件一起编译,新建一个tsconfig.json文件,直接执行tsc命令,全部编译。
编译选项:

"include":["./src/**/*"],//用来指定哪些ts文件需要被编译,src目录下的任意目录任意ts文件
"exclude":["./src/hello/**/*"],//用来排除哪些ts文件不需要编译
"extends":"./config/base",//定义被继承的配置文件
"files":{"core.ts"},//指定被编译文件的列表,只有需要编译的文件比较少时才使用
"compilerOptions":{
    "target":"ES6",//设置ts代码编译的目标版本,默认是转成ES6
    "module":"common.js",//指定要使用的模块化的规范
    "lib":["dom","es6"],//用来指定要使用的库dom等,这个选项最好不改
    "outDir":"./dist",//用来指定编译后文件所在的目录
    "outFile":"./dist/mm.js",//将代码合并成一个文件,和上面的选项比这个优先,引入模块system才能合并,其他模块化不能。
    "allowJs":false,//是否对js文件进行编译,默认false
    "checkJs":false,//是否对js文件进行检查,默认false
    "removeComments":false,//是否移除注释,默认false,注释也会编译
    "noEmit":false,//不生成编译后的文件,默认false,可以用来检查ts语法
    "noEmitOnError":false,//当编译有错时不生成编译后的文件
    
    "strict":true,//所有严格检查的总开关,一般开发true
    "alwayStrict":false,//true开启严格模式,js中开启严格模式是"use strict";,js引入导出模块代码会自动进入严格模式不用写,ts中不用写在这里设置。
    "noImplicitAny":true,//不允许隐式any,默认false
    "noImplicitThis":true,//不允许隐式this(this类型不明确)
    "strictNullChecks":true,//严格检查可能出现为null的调用,let box = document.getElementById("box");box?.addEventListener('clcik',function(){alert('hello');})//可选链
},

5.ts结合webpack

1.初始化,生成package.json
中间可以加上:"build":"webpack"直接使用npm run build 打包

2.下载需要的模块

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

3.编写webpack.config.js

// 引入一个包
const path = require('path');

// webpack中所有的配置信息都写在module.exports中
module.exports = {
    // 指定入口文件
    entry:"./index.ts",

    // 指定打包文件所在目录
    output:{
        path:path.resolve(__dirname,"dist"),
        filename:"bundle.js"
    },
    // 指定webpack打包时要使用的模块
    module:{
        // 指定加载的规则
        rules:[{
            // 指定规则生效的文件
            test:/\.ts$/,
            // 使用ts-loader
            use:'ts-loader',
            exclude:/node_module/
        }]
    },
    mode:'development'
};

4.简单编写tsconfig.js

{
    "compilerOptions":{
        "target":"ES6",
        "module": "ES6",
        "strict": true
    }
}

6.ts结合webpack升级版

前面步骤相同,只是webpack又用到了其他模块

npm i -D html-webpack-plugin    //帮助自动生成html文件
npm i -D webpack-dev-server     //webpack的开发服务器,让项目在该服务器上运行,持续
npm i -D clean-webpack-plugin   //清空打包文件夹

package.json中添加:

"start":"webpack serve"

const path = require('path');
//  引入clean插件
const {CleanWebpackPlugin} = require('clean-webpack-plugin')
// 引入html插件
const HTMLWebpackPlugin = require('html-webpack-plugin');

module.exports = {
   entry:"./index.ts",
   output:{
       path:path.resolve(__dirname,"dist"),
       filename:"bundle.js"
   },
   module:{
       rules:[{
           test:/\.ts$/,
           use:'ts-loader',
           exclude:/node_module/
       }]
   },
   // 配置插件
   plugins:[ 
       new CleanWebpackPlugin(),
       new HTMLWebpackPlugin({
           // title:'自定义title',
           template:"./index.html"
       }),

   ],
   // 配置引入模块
   resolve:{
       extensions:['.ts','.js']
   },
   mode:'development'
};

7.ts结合webpack升级版 兼容性

npm i -D @babel/core @babel/preset-env babel-loader core-js
const path = require('path');
//  引入clean插件
const {CleanWebpackPlugin} = require('clean-webpack-plugin')
// 引入html插件
const HTMLWebpackPlugin = require('html-webpack-plugin');

module.exports = {
    entry:"./index.ts",
    output:{
        path:path.resolve(__dirname,"dist"),
        filename:"bundle.js",
        // 配置打包环境,告诉webpack不要使用箭头函数
        environment:{
            allowFunction:false
        }
    },
    module:{
        rules:[{
            test:/\.ts$/,
            use:[{
                loader:'babel-loader',
                options:{
                    // 设置预定义的环境
                    presets:[
                        [
                            // 指定环境的插件
                        "@babel/preset-env",
                        // 配置信息
                        {
                            // 要兼容的浏览器
                            targets:{
                                "chrome":"58",
                                "ie":"11"//由于webpack自带包裹js代码箭头函数,ie报错
                            },
                            "corejs":"3",
                            // 使用corejs的方式”usage",表示按需加载
                            "useBuiltIns":"usage"
                        }   
                        ]
                     
                    ]
                }
            },'ts-loader'],//加载器执行顺序从后往前,先转为js,再将js转为旧版
            exclude:/node_module/
        }]
    },
    // 配置插件
    plugins:[ 
        new CleanWebpackPlugin(),
        new HTMLWebpackPlugin({
            // title:'自定义title',
            template:"./index.html"
        }),

    ],
    // 配置引入模块
    resolve:{
        extensions:['.ts','.js']
    },
    mode:'production'
};

第二章 TypeScript面向对象

数据称为对象的属性,功能成为对象的方法。

1.类

定义类:

class 类名 extends 父类{//继承父类,子类会拥有父类的方法和属性,如果子类添加了相同的方法会覆盖父类
    //定义实例属性
    属性:类型;
    //定义类属性——静态属性
    static age:number = 18;
    //定义只读属性
    readonly name: string = "张三";
    static readonly ag: number = 18;
    
    //定义实例方法
    sayHello(){console.log("hello");}
    //定义类方法
    static say(){}
    
    //为了用this,每次创建不同的实例
    constructor(name: string,age: number){
        //子类写构造函数,必须调用父类的构造函数
        super(name:string);
        //ts中,this指向当前新建的实例
        //this.name = name;//前面还需要定义实例属性name: string;
        this.age = age;//前面还需要定义实例属性age: number
    }
}

2.抽象类

abstract开头的类,抽象类,不能用来创建实例对象,只能被用来继承。 abstract定义抽象方法,只能写在抽象类中,必须被子类重写。

3.接口

interface开头,用来定义一个类的结构,类种包含哪些属性和方法。
接口中所有的属性都不能有实际的值;
接口中所有的方法都应是抽象方法。

  • 定义两个相同名称的interface,他们会合并。
  • 接口可以在定义类的时候去限制类的结构。
interface myInterface{
    name: string;
    sayHello(): void;
}
class Myclass implements myInterface{
    name: string;
    
    constructor(name: string){
        this.name = name;
    }
    sayHello(){
        console.log("大家好");
    }
}

4.属性修饰符

public 修饰的属性可以在任意位置访问(修改) 默认值。
private 私有属性只能在类内部进行访问修改,通过在类中添加方法使得私有属性可以被外部访问。
protected 受保护的属性,只能在当前类和子类中使用。

可以直接将属性定义在构造函数中:
constructor(public name: string,public age: number){
}
class Myclass{
    private name: string;
    constructor(name: string){
        this.name = name;
    }
    //麻烦的方式,需要调用方法
//    setName(name: string){
//        this.name = name;
//    }
//    getName(){
//        return this.name;
//    }
    
    //设置setter方法
    set name(name: string){
        this.name = name;
    }
    //设置getter方法的方式
    get name(){
         return this.name;
    }
}
const peron = new Myclass('小孩');
console.log(person.name);//实际上是调用了getter方法
person.name = 'hon';//实际上是调用了setter方法

5.泛型

只有函数执行的时候才知道传入的类型,同时传入参数的类型和返回值类型是一致的。

function fn<T>(a: T): T{
    return a;
}
//不指定泛型,利用ts中的自动类型判断机制
fn(10);
//指定泛型
fn<string>('hello');

可以指定多个泛型:

function fn2<T, K>(a: T,b: K):T{
    console.log(b);
    return a;
}
fn2<number,string>(123,'hello');

interface Inter{
    length: number;
}
//T必须是Inter的实现类,即传入的a实参必须是拥有length属性
function fn3<T extends Inter>(a: T):T{
    return a;
}
class Myclass<T>{
    name: T;
    constructor(name: T){
        this.name = name;
    }
}
const mc = new Myclass<string>('孙悟空');