1. TypeScript 简介
1.1 TypeScript 是什么
以 JavaScript 为基础
构建的语言 一个 JavaScript 的超集
TypeScript 扩展
了 JavaScript,并添加了类型
让 JavaScript 从动态类型语言转为静态类型
语言 可以在任何支持 JavaScript 平台中执行
TypeScript 不能被 JavaScript 解析器直接执行
1.2 TypeScript 增加了什么
类型
支持ES新特性
添加ES不具备的新特性
丰富的配置选项
强大的开发工具
2. TypeScript 开发环境搭建
2.1 下载安装 Node.js
2.2 npm 全局安装 TypeScript
终端 npm install -g typescript
2.3 创建 TypeScript 文件
2.4 使用 tsc 进行编译
进入 ts 文件所在目录
执行 tsc xxx.ts
3. TypeScript 基本类型
3.1 类型声明
类型声明是 TypeScript 非常重要的一个特点 通过类型声明可以指定 TS 中变量(参数、形参)的类型
指定类型后,当为变量赋值时,TypeScript 编译器会自动检查值是否符合类型声明,符合则赋值,否则报错
简而言之,类型声明给变量设置了类型,使得变量只能存储某种类型的值
let 变量: 类型;
let 变量: 类型 = 值;
function fn ( 参数: 类型, 参数: 类型) : 类型{
...
}
let a: number ;
a = 10 ;
a = 33 ;
let c = false ;
c = true
function sum ( a: number , b: number ) : number {
return a + b;
}
let result = sum ( 123 , 456 )
3.2 自动类型判断
TypeScript 拥有自动的类型判断机制
当对变量的声明和赋值是同时进行的
,TypeScript 编译器会自动判断变量的类型 所以如果变量的声明和赋值是同时进行
的,可以省略掉类型声明
3.3 类型
类型 例子 描述 number 1, -33, 2.5 任意数字 string ‘hi’, “hi” 任意字符串 boolean true, false 布尔值 字面量 其本身 限制变量的值就是该字面量的值 any * 任意类型 unknown * 类型安全的any void 空值(undefined) 没有值(或undefined) never 没有值 不能是任何值 object {name:‘zx’} 任意的JS对象 array [1,2,3] 任意JS数组 tuple [4,5] 元素,TS新增类型,固定长度数组 enum enum{A,B} 枚举,TS中新增类型
let decimal: number = 6 ;
let hex: number = 0xf00d ;
let binary: number = 0b1010 ;
let octal: number = 0o744 ;
let big: bigint = 100n ;
let isDone: boolean = false ;
let color: string = "blue" ;
color = "red" ;
let fullName: string = "Bob Bobbington" ;
let age: number = 12 ;
let sentence: string = ` Hello, my name is ${ fullName} .
I'll be ${ age + 1 } years old next month. ` ;
let a: 10 ;
a = 10 ;
let b: "male" | "female" ;
b = "male" ;
b = "female" ;
let c: boolean | string;
c = true ;
c = 'hello' ;
let d: any;
d = 10 ;
d = 'hello' ;
d = true ;
let e: unknown;
e = 10 ;
e = 'hello' ;
e = true ;
let s: string;
s = d;
e = 'hello' ;
if ( typeof e === 'string' ) {
s = e;
}
s = e as string;
s = < string> e;
function fn ( ) : void {
return null ;
}
function fn2 ( ) : never {
throw new Error ( "报错了" )
}
let a: object;
a = { } ;
a = function ( ) { }
let b: { name: string, age? : number} ;
b = { name: '孙悟空' , age: 18 }
let c: { name: string, [ propName: string] : any} ;
c = { name: '猪八戒' , age: 18 , gender: '男' } ;
let d : ( a: number, b: number ) => number;
let e: string[ ] ;
e = [ 'a' , 'b' , 'c' ] ;
let f: number[ ] ;
let g: Array< number>
g = [ 1 , 2 , 3 ] ;
let h: [ string, number] ;
h = [ 'hello' , 123 ]
enum Gender {
Male = 0 ,
Female = 1
}
let i: { name: string, gender: Gender} ;
i = {
name: '孙悟空' ,
gender: Gender. Male
}
console. log ( i. gender === Gender. Male) ;
let j: { name: string} & { age: number}
type myType = 1 | 2 | 3 | 4 | 5 ;
let k: myType
4. TypeScript 编译选项
4.1 自动编译文件
编译文件时,使用 -w 指令
后,TS 编译器会自动监视文件的变化
,并在文件发生变化时对文件进行重新编译
示例
4.2 自动编译整个项目
如果直接使用 tsc 指令,则可以自动将当前项目下的所有 ts 文件编译为 js 文件
但是能直接使用 tsc 命令的前提
时,要先在项目根目录下创建一个 ts 的配置文件 tsconfig.json
tsconfig.json 是一个 JSON 文件
,添加配置文件
后,只需 tsc 命令即可完成对整个项目的编译
4.2.1 配置选项 include
定义希望被编译文件所在的目录
默认值: ["**/*"]
示例
"include":["src/**/*","tests/**/*"]
上述示例中,所有 src 目录和 tests 目录下的文件都会被编译
4.2.2 配置选项 exclude
exclude
定义需要排除在外的目录
默认值: ["node_modules", "bower_components","jspm_packages"]
示例
"exclude": ["./src/hello/**/*"]
上述示例中,src 下 hello 目录下的文件都不会被编译
4.2.3 配置选项 extends
extends
定义被继承的配置文件
示例:
"extends": "./configs/base"
上述示例中,当前配置文件中会自动包含 config 目录下 base.json 中的所有配置信息
4.2.4 配置选项 files
files
指定被编译文件的列表,只有需要编译的文件少时才会用到
示例:
"files" : [
"core.ts" ,
"sys.ts" ,
"types.ts" ,
"scanner.ts" ,
"parser.ts" ,
"utilities.ts" ,
"binder.ts" ,
"checker.ts" ,
"tsc.ts"
]
4.2.5 配置选项 compilerOptions
编译选项
是配置文件中非常重要也比较复杂的配置选项在 compilerOptions
中包含多个子选项
,用来完成对编译的配置
项目选项
target
设置 ts 代码编译的目标版本
可选值
:
ES3(默认)、ES5、ES6/ES2015、ES7/ES2016、ES2017、ES2018、ES2020、ESNext 示例
如上设置,我们所编写的 ts 代码将会被编译为 ES6 版本的 js 代码
"compilerOptions": {"target": "ES6"}
lib
指定代码运行时所包含的库(宿主环境)
可选值
:
ES5、ES6/ES2015、ES7/ES2016、ES2017、ES2018、ES2019、ES2020、ESNext、DOM、WebWorker、ScriptHost… 示例
"compilerOptions": {"target": "ES6", "lib": ["ES6", "DOM"], "outDir": "dist", "outFile": "dist/aa.js"}
module
设置编译后代码使用的模块化系统
可选值
:
CommonJS、UMD、AMD、System、ES2020、ESNext、None 示例
compilerOptions": {"module": "CommonJS"}
outDir
编译后文件的所在目录
默认情况下,编译后的 js 文件会和 ts 文件位于相同的目录
,设置 outDir 后可以改变编译后文件的位置
示例:
设置后编译后的 js 文件将会生成到 dist 目录
"compilerOptions": {"outDir": "dist"}
outFile
将所有的文件编译为一个js文件
默认会将所有的编写在全局作用域中的代码合并为一个 js 文件
,如果 module 制定了 None、System 或 AMD 则会将模块一起合并到文件之中
示例:
"compilerOptions": {"outFile": "dist/app.js"}
rootDir
指定代码的根目录
,默认情况下编译后文件的目录结构会以最长的公共目录为根目录
,通过rootDir 可以手动指定根目录
示例:
"compilerOptions": {"rootDir": "./src"}
allowJs
checkJs
是否对 js 文件进行检查
示例:
"compilerOptions": {"allowJs": true, "checkJs": true}
removeComments
noEmit
sourceMap
严格检查
选项名称 选项功能 strict 启用所有的严格检查,默认值为 true,设置后相当于开启了所有的严格检查 alwaysStrict 总是以严格模式对代码进行编译 noImplicitAny 禁止隐式的 any 类型 noImplicitThis 禁止类型不明确的 this strictBindCallApply 严格检查 bind、call 和 apply 的参数列表 strictFunctionTypes 严格检查函数的类型 strictNullChecks 严格的空值检查 strictPropertyInitialization 严格检查属性是否初始化
选项名称 选项功能 noFallthroughCasesInSwitch 检查 switch 语句包含正确的 break noImplicitReturns 检查函数没有隐式的返回值 noUnusedLocals 检查未使用的局部变量 noUnusedParameters 检查未使用的参数
高级
allowUnreachableCode
检查不可达代码
可选值:
true,忽略不可达代码
false,不可达代码将引起错误
noEmitOnError
5. 使用 webpack 打包 TypeScript 代码
5.1 webpack 整合
5.1.1 初始化项目
进入项目根目录,执行命令 npm init -y
,创建 package.json
文件
5.1.2 下载构建工具
npm i -D webpack webpack-cli webpack-dev-server typescript ts-loader clean-webpack-plugin
安装包:
安装包名称 作用 webpack 构建工具 webpack webpack-cli webpack 的命令行工具 webpack-dev-server webpack 的开发服务器 typescript ts 编译器 ts-loader ts 加载器,用于在webpack中编译ts文件 html-webpack-plugin webpack 中 html 插件,用来自动创建 html 文件 clean-webpack-plugin webpack 中的清除插件,每次构建都会先清除目录
5.1.3 配置 webpack
const path = require ( "path" ) ;
const HtmlWebpackPlugin = require ( "html-webpack-plugin" ) ;
const { CleanWebpackPlugin } = require ( "clean-webpack-plugin" ) ;
module. exports = {
optimization: {
minimize: false
} ,
entry: "./src/index.ts" ,
devtool: "inline-source-map" ,
devServer: {
contentBase: './dist'
} ,
output: {
path: path. resolve ( __dirname, "dist" ) ,
filename: "bundle.js" ,
environment: {
arrowFunction: false
}
} ,
resolve: {
extensions: [ ".ts" , ".js" ]
} ,
module: {
rules: [
{
test: / \.ts$ / ,
use: {
loader: "ts-loader"
} ,
exclude: / node_modules /
}
]
} ,
plugins: [
new CleanWebpackPlugin ( ) ,
new HtmlWebpackPlugin ( {
title: 'TS测试'
template: "./src/index.html"
} ) ,
]
}
5.1.4 配置 TS 编译选项
{
"compilerOptions" : {
"target" : "ES2015" ,
"module" : "ES2015" ,
"strict" : true
}
}
5.1.5 修改 package.json 配置
{
...
"scripts" : {
"test" : "echo \"Error: no test specified\" && exit 1" ,
"build" : "webpack" ,
"start" : "webpack serve --open chrome.exe"
} ,
...
}
5.1.6 项目使用
执行npm run build
对 ts 代码进行编译 或者执行npm start
来启动开发服务器
5.2 Babel
除了webpack,开发中还经常需要结合babel
来对代码进行转换 以使其可以兼容到更多的浏览器
,在上述步骤的基础上,通过以下步骤再将babel引入到项目中
虽然TS在编译时也支持代码转换,但是只支持简单的代码转换
对于例如:Promise等ES6特性,TS无法直接转换,这时还要用到babel来做转换
安装依赖包
npm i -D @babel/core @babel/preset-env babel-loader core-js
安装包名称 作用 @babel/core babel 的核心工具 @babel/preset-env babel 的预定义环境 @babel-loader babel 在 webpack 中的加载器 core-js core-js 用来使老版本的浏览器支持新版ES语法
...
module: {
rules: [
{
test: / \.ts$ / ,
use: [
{
loader: "babel-loader" ,
options: {
presets: [
[
"@babel/preset-env" ,
{
"targets" : {
"chrome" : "58" ,
"ie" : "11"
} ,
"corejs" : "3" ,
"useBuiltIns" : "usage"
}
]
]
}
} ,
{
loader: "ts-loader" ,
}
] ,
exclude: / node_modules /
}
]
}
...
如此一来,使用ts编译后的文件将会再次被babel处理
使得代码可以在大部分浏览器中直接使用
同时可以在配置选项的 targets 中指定要兼容的浏览器版本
6. TypeScript 面向对象
程序之中所有的操作都需要通过对象来完成,也就是面向对象
举例
操作浏览器
要使用 window
对象 操作网页
要使用 document
对象 操作控制台
要使用 console
对象 计算机程序的本质就是对现实事物的抽象
,事物在程序中也就变成了对象在程序中所有的对象都被分为两个部分,数据和功能
举例
人的姓名、性别、年龄、身高、体重属于数据
吃饭、睡觉属于功能或方法
6.1 定义类
要想面向对象
,操作对象
,首先要拥有对象
要创建对象
,必须要先定义类
,所谓的类可以理解为对象的模型
程序中可以根据类创建指定类型的对象
举例
可以通过Person
类来创建人的对象,通过Dog
类创建狗的对象,不同的类可以用来创建不同的对象
class 类名 {
属性名: 类型;
constructor ( 参数: 类型) {
this . 属性名 = 参数;
}
方法名 ( ) {
... .
}
}
class Person {
name = '孙悟空' ;
age = 18 ;
constructor ( name: string , age: number ) {
this . name = name;
this . age = age;
}
sayHello ( ) {
console . log ( ` 大家好,我是 ${ this . name} ` ) ;
}
}
const p = new Person ( '孙悟空' , 18 ) ;
p. sayHello ( ) ;
6.2 构造函数
可以使用constructor
定义一个构造器方法
在TS中只能有一个构造器方法
例如
class C {
name: string ;
age: number
constructor ( name: string , age: number ) {
this . name = name;
this . age = age;
}
bark ( ) {
console . log ( this . name) ;
}
}
class C {
constructor ( public name: string , public age: number ) {
}
}
上面两种定义方法是完全相同
的 子类继承父类时,必须调用父类的构造方法(如果子类中也定义了构造方法)
例如
class A {
protected num: number ;
constructor ( num: number ) {
this . num = num;
}
}
class X extends A {
protected name: string ;
constructor ( num: number , name: string ) {
super ( num) ;
this . name = name;
}
}
6.3 封装
对象实质上就是属性和方法的容器
,它的主要作用就是存储属性和方法
,这就是所谓的封装
默认情况下,对象的属性是可以任意的修改的
,为了确保数据的安全性
,在TS中可以对属性的权限进行设置
静态属性(static)
:
声明为static的属性或方法不再属于实例,而是属于类的属性
只读属性(readonly)
:
如果在声明属性时添加一个readonly,则属性便成了只读属性无法修改
TS中属性具有三种修饰符
:
public(默认值),可以在类、子类和对象中修改
protected ,可以在类、子类中修改
private ,可以在类中修改
示例
public
class Person {
public name: string ;
public age: number ;
constructor ( name: string , age: number ) {
this . name = name;
this . age = age;
}
sayHello ( ) {
console . log ( ` 大家好,我是 ${ this . name} ` ) ;
}
}
class Employee extends Person {
constructor ( name: string , age: number ) {
super ( name, age) ;
this . name = name;
}
}
const p = new Person ( '孙悟空' , 18 ) ;
p. name = '猪八戒' ;
class Person {
protected name: string ;
protected age: number ;
constructor ( name: string , age: number ) {
this . name = name;
this . age = age;
}
sayHello ( ) {
console . log ( ` 大家好,我是 ${ this . name} ` ) ;
}
}
class Employee extends Person {
constructor ( name: string , age: number ) {
super ( name, age) ;
this . name = name;
}
}
const p = new Person ( '孙悟空' , 18 ) ;
p. name = '猪八戒' ;
class Person {
private name: string ;
private age: number ;
constructor ( name: string , age: number ) {
this . name = name;
this . age = age;
}
sayHello ( ) {
console . log ( ` 大家好,我是 ${ this . name} ` ) ;
}
}
class Employee extends Person {
constructor ( name: string , age: number ) {
super ( name, age) ;
this . name = name;
}
}
const p = new Person ( '孙悟空' , 18 ) ;
p. name = '猪八戒' ;
6.3.1 属性的封装
( function ( ) {
class Person {
name: string;
age: number;
constructor ( name: string, age: number ) {
this . name = name;
this . age = age;
}
getName ( ) {
return this . name
}
setName ( value: string ) {
this . name = value;
}
get name ( ) {
console. log ( 'zzz' )
return this . name
}
set name ( value: string ) {
this . name = value
}
}
const per = new Person ( 'xxx' , 12 ) ;
per. setName ( 'qqq' )
console. log ( per. getName ( ) )
class A {
protected num: number;
constructor ( num: number ) {
this . num = num;
}
}
class B extends A {
test ( ) {
console. log ( this . num)
}
}
class C {
constructor ( public name: string, public age: num) {
}
}
} ) ( ) ;
6.4 属性存取器
对于一些不希望被任意修改的属性,可以将其设置为 private
直接将其设置为 private 将导致无法再通过对象修改其中的属性
我们可以在类中定义一组读取、设置属性的方法
,这种对属性读取或设置的属性被称为属性的存取器
读取属性的方法叫做 setter 方法,设置属性的方法叫做 getter 方法
示例
class Person {
private _name: string ;
constructor ( name: string ) {
this . _name = name;
}
get name ( ) {
return this . _name;
}
set name ( name: string ) {
this . _name = name;
}
}
const p1 = new Person ( '孙悟空' ) ;
console . log ( p1. name) ;
p1. name = '猪八戒' ;
6.5 静态属性
静态属性(方法),也称为类属性
使用静态属性无需创建实例
,通过类即可直接使用
静态属性(方法)使用 static 开头
示例
class Tools {
static PI = 3.1415926 ;
static sum ( num1: number , num2: number ) {
return num1 + num2
}
}
console . log ( Tools. PI ) ;
console . log ( Tools. sum ( 123 , 456 ) ) ;
6.6 this
6.7 继承
继承是面向对象中的又一个特性 通过继承可以将其他类中的属性和方法引入到当前类中
通过继承可以在不修改类的情况下完成对类的扩展
示例
( function ( ) {
class Animal {
name: string ;
age: number ;
constructor ( name: string , age: number ) {
this . name = name;
this . age = age;
}
sayHello ( ) {
console . log ( 'xxx' ) ;
}
}
class Dog extends Animal {
run ( ) {
...
}
}
class Cat extends Animal {
}
const dog = new Dog ( 'xx' , 5 ) ;
console . log ( dog) ;
dog. sayHello ( ) ;
dog. run ( ) ;
const cat = new Cat ( 'xx' , 5 ) ;
console . log ( cat) ;
cat. sayHello ( ) ;
cat. run ( )
} ) ( ) ;
6.7.1 重写
发生继承时,如果子类中的方法会替换掉父类中的同名方法
,这就称为方法的重写
在子类中可以使用 super 来完成对父类的引用
示例
class Animal {
name: string ;
age: number ;
constructor ( name: string , age: number ) {
this . name = name;
this . age = age;
}
run ( ) {
console . log ( ` 父类中的run方法! ` ) ;
}
}
class Dog extends Animal {
bark ( ) {
console . log ( ` ${ this . name} 在汪汪叫! ` ) ;
}
run ( ) {
console . log ( ` 子类中的run方法,会重写父类中的run方法! ` ) ;
}
}
const dog = new Dog ( '旺财' , 4 ) ;
dog. bark ( ) ;
6.7.2 super 关键字
class Animal {
name: string ;
age: number ;
constructor ( name: string , age: number ) {
this . name = name;
this . age = age;
}
sayHello ( ) {
console . log ( ` 父类中的sayHello方法! ` ) ;
}
}
class Dog extends Animal {
constructor ( color: string ) {
super ( name, age) ;
this . color = color;
}
sayHello ( ) {
super . sayHello ( ) ;
}
}
const dog = new Dog ( '旺财' , 4 ) ;
dog. sayHello ( ) ;
6.7.3 抽象类(abstract class)
抽象类是专门用来被其他类所继承的类,它只能被其他类所继承,不能用来创建实例
使用 abstract 开头的方法叫做抽象方法,抽象方法没有方法体只能定义在抽象类中,继承抽象类时抽象方法必须要实现
( function ( ) {
abstract class Animal {
name: string ;
constructor ( name: string ) {
this . name = name;
}
abstract sayHello ( ) : void ;
}
class Dog extends Animals {
sayHello ( ) {
console . log ( '狗在跑~' ) ;
}
}
class Cat extends Animal {
sayHello ( ) {
console . log ( 'mmm' ) ;
}
}
} ) ( ) ;
7. TypeScript 接口
接口的作用类似于抽象类
不同点在于:接口中的所有方法和属性都是没有实值的
,即接口中的所有方法都是抽象方法
接口主要负责定义一个类的结构
,接口可以去限制一个对象的接口
对象只有包含接口中定义的所有属性和方法时才能匹配接口
同时,可以让一个类去实现接口,实现接口时类中要保护接口中的所有属性
示例
( function ( ) {
type myType = {
name: string ,
age: number
} ;
interface myInterface {
name: string ;
age: number ;
}
interface myInterface {
gender: string ;
}
const obj: myInterface = {
name: 'sss' ;
age: 111 ,
gender: '男'
}
interface myInter {
name: string ;
sayHello ( ) : void ;
}
class MyClass implements myInterface {
name: string ;
constructor ( name: string ) {
this . name = name;
}
sayHello ( ) {
console . log ( 'xxx' )
}
}
} ) ( ) ;
8. TypeScript 泛型
定义一个函数或类时,有些情况下无法确定其中要使用的具体类型(返回值、参数、属性的类型不能确定)
此时泛型便能够发挥作用 举例
function test ( arg: any ) : any{
return arg;
}
8.1 泛型函数
8.1.1 创建泛型函数
function test< T > ( arg: T ) : T {
return arg;
}
这里的<T>
就是泛型
T是我们给这个类型起的名字(不一定非叫T),设置泛型后即可在函数中使用T来表示该类型
所以泛型其实很好理解,就表示某个类型
; 那么如何使用上边的函数呢?
8.1.2 使用泛型函数
test ( 10 )
test< number> ( 10 )
function test< T , K > ( a: T , b: K ) : K {
return b;
}
test< number, string> ( 10 , "hello" ) ;
8.2 泛型类
class MyClass < T > {
prop: T ;
constructor ( prop: T ) {
this . prop = prop;
}
}
8.3 泛型继承
interface MyInter {
length: number;
}
function test< T extends MyInter > ( arg: T ) : number{
return arg. length;
}
9. 总结
看了李立超老师的 TypeScript 教程,做个笔记,总结一下