第一章 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;
③unknown与any不一样,不能赋值给其他类型的变量,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>('孙悟空');