第一章 TS入门
1.1 TS简介
TS是JS的超集,由微软设计。JS是动态类型,不易于维护,而TS是静态类型语言。
TS新增:更多的类型(云组、抽象类等),具有ES不具有的特性(装饰器、接口等),可以编译成不同版本的JS,大大增强了兼容性。
1.2 第一个程序Hello,TS!
由于TS是JS的超集,因此所有JS能用的写法TS都能使用。
在HelloTS.ts文件中想打印依然使用的是console.log方法。
console.log('Hello,TS')
利用ts的编译器(使用前需要先利用命令npm i typescript -g全局下载),就可以将ts代码编译为ts代码,仅需在终端中输入命令tsc HelloTS.ts即可,最终会生成一个HelloTS.js文件,默认情况下,即使在ts中有错误(例如将字符串赋值给了一个number类型的变量),但依旧能编译成功生成相应的js文件,并且默认为es3的js(例如,let的声明变成var声明),想要更改js版本与编译的方式,需要对ts的编译器进行配置。
1.3 TS类型声明
1.3.1 基本类型
let n: number = 1;
let s: string ='s';
let boolen: boolean = true;
// 对一个变量设置成any类型后,相当于对这个变量关闭了ts检测
let ay: any; // 相当于 let ay(隐式声明any类型)
// unknown表示未知变量
let uk: unknown; uk = 1; uk = 's'; // s = uk 会报错 unknown类型不能给别的类型赋值
if(typeof uk === 'string') {
s = uk; // 因此unknown可以视作安全类型的any
}
s = uk as string; // 或者使用断言
s = <string> uk; // 断言的第二种写法
// 字面量声明 |表示‘或’(联合类型)
let a: 'male' | 'female' = 'male'; // a = 'man' 报错
// ts会自动判断类型,当声明时没声明类型时会自动判断其类型值
let bl = true;
// bl = 123 // 报错
// 函数的类型声明
function sum(a: number, b:number): number{
return a + b;
}
let res = sum(123, 456);
// void表示无返回值
function fn1(): void{
// return null 不表示无返回值哦 表示返回一个空对象
return undefined //这个可以 可以理解void就是返回undefined
}
// never表示永远不会返回结果
function fn2(): never{
throw new Error('报错了'); // never的一般用法,扔出错误,程序停止不返回任何结果
// return undefined 会报错
}
1.3.2 对象类型
let obj: object = {} // 可以是任意对象(不常用)因为js万物皆对象
obj = function() {} // 不会报错 因为函数也是对象(逆天)
// 定义对象,其中必须只有一个string类型的name属性
// age属性是可选的
let b: {name: string, age?: number}
b = {name: 'b', age: 18}
// [propName: string]表示还有任意字符串的属性名,而:any指的是该属性可以是任意类型
let c: {name: string, [propName: string]: any};
c = {name: 'c', age: 18, sex: '男'};
// 定义函数类型 (形参1:类型,形参2:类型)=>类型;
let d: (a: number, b: number)=>number;
d = function(n1, n2): number{
return n1 + n2;
}
// 声明相同类型的数组 两种表达方式
let e: string[];
let g: Array<string>;
// tuple 元组:长度固定的数组(效率高)
let h: [string, string];
// enum 枚举(效率高)
enum Gender {
Male = 0,
Female = 1
}
let i: {name: string, gender: Gender} = {
name: 'zzl',
gender: Gender.Male
}
// & 表示与 一般用于两个对象的合并
let j: {name: string} & {age: number};
j = {name: 'zzl', age: 21}
// type 关键字取别名
type myType = 1 | 2 | 3 | 4 |5;
let k: myType;
1.4 TS编译设置
tsc xxx.ts -w开启监视模式,当在TS文件中更改时,JS文件会自动更改(会有一定的延迟)。 直接使用tsc命令可以编译文件夹下所有的ts文件,但必须先配置tsconfig.json文件才行(直接运行指令tsc --init也能初始化tsconfig.json文件)。 使用指令tsc -w可以开启文件夹下的全局监听模式。
tsconfig.json文件配置具体信息
- include:数组,用来指定哪些ts文件需要被编译
- exclude:数组,用来指定哪些ts文件被排除在外不进行编译。
- compileOptions:用来配置编译器如何进行编译,其中包含众多子选项(若不知道要写什么,可以故意写错看终端报错)
- target:指定js编译的默认版本,例如es3,es2016,esnext(最新选项)等
- module:指定js模块化的规范,例如commonjs,es2016,esnext等
- lib:指定项目要使用的库(一般不会更改)
- outDir:指定文件编译后的目录
- sourceMap:用于指定是否为编译后的JavaScript代码生成源映射(SourceMap)文件
- outFile:将全局作用域的代码合并为一个文件(将多个ts文件合并为一个js文件)
- allowJs:将js文件一同编译,默认为false
- checkJs:检查js代码是否符合ts规范,默认为false
- removeComments:是否移除注释,默认为false
- noEmit:不生成编译后的文件,默认为false(用来检查语法对不对)
- noEmitOnError:当有错误时不生成编译后文件,默认为false
- esModuleInterop:使在ES模块系统中导入CommonJs代码不出现类型不兼容的问题
- forceConsistentCasingInFileNames:检查文件编译时文件名的大小写是否一致
- strict:所有严格检测的总开关(设置为true,下方的严格检查的都会打开)
- alwaysStrict:编译后的js设置为严格模式,默认为false
- noImplicitAny:不允许隐式的any类型,默认为false
- noImplicitThis:不允许不明确类型的this(this必须有所指)
- strictNullChecks:严格检查空值(检查是否会出现空指针)
示例代码:
{
// "include”用来指定哪些ts文件需要被编译,**表示任意目录,*表示任意文件
"include": [
"./src/**/*"
],
"exclude": [
"./src/hello/**/*"
],
// 编译器选项
"compilerOptions": {
// 指定js编译的版本
"target": "es2016",
// 指定js的模块化规范
"module": "commonjs",
// 指定项目中用倒的库,当设置后有快捷键显示
"lib": [],
// outDir指定文件编译后的目录
"outDir": "./dist",
// 将全局作用域的代码合并为一个文件(将多个ts文件合并为一个js文件)
"outFile": "./dist/app.js",
// 将js文件一同编译,默认为false
"allowJs": false,
// 用于指定是否为编译后的JavaScript代码生成源映射(SourceMap)文件
"sourceMap": false,
// 检查js代码是否符合ts规范,默认为false
"checkJs": false,
// 是否移除注释,默认为false
"removeComments": false,
// 不生成编译后的文件,默认为false(用来检查语法对不对)
"noEmit": false,
// 当有错误时不生成编译后文件,默认为false
"noEmitOnError": false,
// 所有严格检测的总开关(设置为true,下方的严格检查的都会打开)
"strict": true,
// 编译后的js设置为严格模式,默认为false
"alwaysStrict": false,
// 不允许隐式的any类型,默认为false
"noImplicitAny": false,
// 不允许不明确类型的this
"noImplicitThis": false,
// 严格检查空值(检查是否会出现空指针)
"strictNullChecks": false,
// 使在ES模块系统中导入CommonJs代码不出现类型不兼容的问题
"esModuleInterop": true,
// 检查文件编译时文件名的大小写是否一致
"forceConsistentCasingInFileNames": true,
}
}
1.5 使用webpack打包ts打代码
安装依赖指令:npm i -D webpack webpack-cli typescript ts-loader
在项目中新建一个webpack.config.js作为webpack的配置文件,用来指定需要打包的文件
// 引入内置的path模块
const path = require('path');
// webpack的配置信息都写在module.exports之中
module.exports = {
// 指定入口文件
entry: "./src/index.ts",
// 指定打包完文件所在的目录
output: {
// 指定打包文件的目录
path: path.resolve(__dirname, 'dist'),
// 打包后文件的文件
filename: 'bundle.js',
// 配置打包环境
environment: {
arrowFunction: false // 设置打包生成的js文件不使用箭头函数
}
},
// 指定webpack打包时要使用的模块
module: {
// 指定加载时的规则
rules: [
{
// 指定规则生效的文件(正则表达式)
test: /\.ts$/,
// 对文件如何处理
use: "ts-loader",
// 要排除的文件
exclude: /node_modules/
}
]
}
}
之后在src中创建一个tsconfig文件,配置好所有东西后再在package.json文件中的script中的命令中加入"build":"webpack",就可以输入命令npm run bulid进行打包啦。
由webpack创建index.html文件:
首先使用命令npm i -D html-webpack-plugin,下载相关包,之后在webpack配置文件中引入包并配置插件
const HTMLWEBPACKPLUGIN = require('html-webpack-plugin')
...
// 配置插件
plugins: [
new HTMLWEBPACKPLUGIN({
//title: "这是一个自定义的标签"
template: "./src/index.html" // 模版文件,生成时以此为模板
}),
]
由webpack配置开发服务器:
首先使用命令npm i -D webpack-dev-server,下载相关包,之后在package.json中的script中添加 "start": "webpack server --open"之后使用命令npm start即可打开服务器(运行前需要在webpack的配置文件中配置mode选项才行),并实现热更新。 由webpack编译时自动清空原来dist目录中的文件:
首先使用命令npm i -D html-webpack-plugin,下载相关包,之后在webpack配置文件中引入包并配置插件
const { CleanWebpackPlugin } = require('clean-webpack-plugin')
...
// 配置插件
plugins: [
new CleanWebpackPlugin()
]
配置ts的模块化:
默认情况下,ts文件是不能作为引入模块的,因此需要在webpack之中进行配置:
// 用来配置引用模块
resolve: {
// 以这两者结尾都可以作为模块使用
extensions: ['.ts', '.js']
}
引入babel:
ts可以替换简单的语法,但不能转换Promise等ES6新出的东西。因此需要引入babel,使用命令npm i -D @babel/core @babel/preset-env babel-loader core-js来下载babel,之后在webpack.config.json的module之中进行修改,代码如下所示:
// 对文件如何处理,加载器由下往上进行执行
use: [
// 配置babel
{
// 指定加载器
loader: 'babel-loader',
// 设置babel
options: {
// 配置babel的环境
presets: [
[
// 指定环境的插件
'@babel/preset-env',
// 配置信息
{
targets: {
// 指定兼容的目标浏览器
"chrome": "87",
"ie": "11"
},
// 指定core.js的版本
"corejs": "3",
// 使用core.js的模式,“usage”表示按需加载
"useBuiltIns": "usage"
}
]
]
}
},
"ts-loader"
],
当打包时,ts文件先去找ts-loader编译为js文件,再去找babel转换为指定版本的js文件。
引入less的依赖:
安装各个包npm i -D less less-loader css-loader style-loader,之后在webpack.config.json中进行设置(在module中新增一个规则),实际上在webpack中,样式是通过js生效的。
// 指定less文件的处理
{
test: /\.less$/,
use: [
"style-loader",
"css-loader",
"less-loader"
]
}
成功配置好less之后,还需要引入postcss来实现对css文件的转义来适应不同的浏览器(类似于babel对js文件一样)。命令npm i -D postcss postcss-loader postcss-preset-env下载完成后,在webpack的配置文件中继续修改module,代码如下:
use: [
"style-loader",
"css-loader",
// 引入postcss
{
loader: "postcss-loader",
options: {
postcssOptions: {
plugins: [
[
"postcss-preset-env",
{
browsers: 'last 2 versions'
}
]
]
}
}
},
"less-loader"
]
第二章 面向对象
2.1 在ts中定义类
示例代码:
class Person {
// 定义实例属性
name: string = '孙悟空';
age: number = 18;
// 定义静态属性
static DNA: string;
// 只读属性
readonly ID: string = '123456';
// 定义实例方法
sayHello() {
console.log('Hello,大家好!');
}
}
2.2 构造函数与继承与抽象类
示例代码:
abstract class Animal {
name: string;
constructor(name: string) {
// 在示例方法中,this指向当前的实例
this.name = name;
};
// 抽象方法只能定义在抽象类中,没有方法体子类必须重写
abstract sayHello(): void;
}
class Dog extends Animal {
age: number;
constructor(name: string, age: number) {
// 子类使用构造函数时必须写
super(name);
this.age = age;
};
// 添加方法
run(): void {
console.log('跑');
};
sayHello(): void {
// 在类中,super表示当前类的父类
// super.sayHello(); 会报错,不能使用抽象方法
// 方法重写
console.log('汪汪汪');
};
}
class Cat extends Animal {
sayHello(): void {
// 方法重写
console.log('喵喵喵');
};
}
2.3 接口
接口中是ts新增的,注意:接口还可以给函数使用,示例代码:
type myType = {
name: string,
age: number
}
const obj1: myType = {
name: '111',
age: 11
}
// 接口类似于type关键字,用来限制一个类的结构(类似抽象类)
interface myInterface {
name: string,
age: number
}
// 接口可以重复定义,最终实现便是两者之并集
interface myInterface {
gender: '男' | '女'
}
const obj2: myInterface = {
name: '111',
age: 11,
gender: '男'
}
// 接口只定义的只能是抽象的,不能有实际值(不同于抽象类)
interface myInter {
name: string,
sayHello(): void
}
// 定义类来实现一个接口
class MyClass implements myInter {
name: string;
constructor(name: string) {
this.name = name;
}
sayHello(): void {
console.log('Hello');
}
}
// 利用接口方便函数的定义
interface IMult {
(x: number, y: number): number;
}
const mult1:IMult = (x, y) => x*y;
const mult2: (x: number, y: number) => number = (x, y) => x*y;
2.4 属性的封装
ts中新增了public,protected,private属性用来修饰成员变量,从未实现对属性的封装,示例代码:
class Person {
public name: string; // 公开的
protected age: number; // 只能在该类中与继承的子类中访问
private _ID: string; // 只能在该类中访问
constructor(name:string, age:number, ID: string) {
this.name = name;
this.age = age;
this._ID = ID;
}
// 通过定义方法来使外部访问私有成员变量
// getID(): string {
// return this._ID
// }
// setID(ID: string): void {
// this._ID = ID;
// }
// TS中设置相应的getter与setter
get ID() {
console.log('get ID执行了!');
return this._ID;
}
set ID(ID: string) {
this._ID = ID;
}
}
// 简写方式(语法糖)
class People {
constructor(private ID: string, protected age: number, public name: string) {}
}
const per = new Person('孙悟空', 18, '123456');
const peo = new People('孙悟空', 18, '123456')
console.log(per.ID);
2.5 泛型
当传入的值不晓得时(函数或者类),可以使用泛型(即不确定的类型),必须先定义泛型,之后才能使用泛型,示例代码如下:
// 简单的函数(返回传入的值),但看不出来返回的是a(可读性不强)
// function fn(a: number): number {
// return a;
// }
// 当传入的值不晓得时(函数或者类),可以使用泛型(即不确定的类型)
// 必须先定义泛型,之后才能使用泛型
function fn1<T>(a: T): T {
return a;
}
fn1(10); // 不指定泛型,js自动推断(当数据结构复杂时可能报错)
fn1<String>('Hello') // 指定泛型
// 泛型可以指定多个
function fn2<T, K>(a: T, b: K): T {
console.log(b);
return a;
}
fn2<Number, String>(11, 'hello');
interface Inter {
length: number;
}
// 指定泛型必须是Inter的子类
function fn3<T extends Inter>(a: T): number {
return a.length;
}
fn3({length: 12})
// fn3({name: '111'}) // 会报错
// 在类中使用泛型
class Test<T> {
name: T;
constructor(name: T) {
this.name = name;
}
}
const test = new Test('孙悟空');