文章目录
- TypeScript学习笔记
- 一、TS简介
- 1. 学习前提
- 2. TypeScript是什么?
- 3. TypeScript增加了什么?
- 二、TS开发环境搭建
- 1. 下载、安装Node.js
- 2. npm安装TypeScript
- 3. 创建一个TS文件,使用tsc对TS文件进行编译
- 三、TS的类型
- 1. 类型声明
- 2. 类型大全
- 2.1 字面量
- 2.2 any / unknown
- 2.3 void / never
- 2.4 object
- 2.5 array
- 2.6 tuple
- 2.7 enum
- 2.8 其它
- 四、编译选项
- 1. 监视
- 2. tsconfig.json
- 3. 配置选项
- 3.1 include / exclude
- 3.2 extends files
- 3.3 CompilerOptions【重要】
- 3.3.1 target
- 3.3.2 module
- 3.3.3 lib
- 3.3.4 outDir outFile
- 3.3.5 allowJs checkJs
- 3.3.5 removeComments noEmit noEmitError
- 3.3.6 alawaysStrict noImplicitAny noImplicitThis
- 3.3.7 strictNullChecks strict
- 五、webpack打包ts代码
- 1.初始化项目:
- 2. 下载构建工具
- 3.创建`webpack.config.js`并编写
- 4. tsconfig.json文件配置
- 5. 补充
- 六、面向对象
- 1. 类class
- 2. 构造函数和this
- 3. 继承
- 4. 抽象类
- 5. 接口
- 6. 属性的封装
- 7. 泛型
- 总结
TypeScript学习笔记
一、TS简介
1. 学习前提
学习前端必备语言javascript,它易学易用,开发过程中挖坑也容易(/狗头),维护起来比较难受,不太适合开发大型项目。
JS变量是一个动态类型,比如说let a = 123
,后面也可以给a赋值为字符串、布尔值等等。
JS函数的参数没有类型限制,很自由也不报错,容易挖坑。
JS很好,但是也存在一些缺点让人头痛。
我们希望有一个新的语言,来弥补一些缺点,而不是替代JS。微软设计的TypeScript
就很不错。
2. TypeScript是什么?
TS是以 JS 为基础构建的语言,是 JS 的一个超集 。
TS 扩展了 JS 并添加了类型,可以在支持JS的平台上执行。可能你在书写 TS 的时候觉得它事儿多,但是习惯之后会觉察出它的优势的,坚持下去。
TS 目前不能被 JS 解析器直接执行,我们要将 TS 编译成 JS 执行。
3. TypeScript增加了什么?
大体列举一下,后续会进行学习,不着急不着急
- 类型
- 支持ES的新特性
- 添加ES不具备的新特性
- 丰富的配置选项
- 强大的开发工具
二、TS开发环境搭建
1. 下载、安装Node.js
系统最好是64位的因为这样内存会大一些。
node.js官网下载即可,用LTS
这种长期维护版本,安装路径尽量不要有特殊符号和中文。
查看node版本命令:node -v
2. npm安装TypeScript
查看npm版本命令:npm -v
npm安装TS : npm i -g typescript
,会有点慢,呆胶布,我会等。
检查:输入tsc
,出现一堆乱七八糟,说明OK了
3. 创建一个TS文件,使用tsc对TS文件进行编译
推荐的是VSCode编辑器书写代码
我们都知道编辑器它不认ts文件呢,现在我们用**tsc
编译器**编译一下,目录下会生成一个js文件
网页中引入js文件就可以执行了
三、TS的类型
1. 类型声明
TS 和 JS 的一个区别就是 TS 的变量有类型的概念
在 js 里我们可以给变量 a 任意赋值,可能给后期维护挖了大坑
let a;
a = 'hello';
a = 2;
那我们在TS中怎么做呢?
//声明一个变量,同时指定它的类型为number数字,
let a:number
a = 1 ; //不报错
a = '' //报错
此时 a 只能赋值数字,要是赋值其他的类型将会报错
声明完变量直接赋值:
let c:boolean = true //如果声明和赋值同时进行,那TS可以自动对变量进行类型检测
let c = false //可以这样简写
函数参数类型:
function sum(a:number,b:number){
return a + b
}
sum(a:123,b:'123') //此时 b 会报错,相比于 js 能让人更好的发现问题从而进行修改,参数要是传多了也会报错。
返回值类型:
function sum(a:number,b:number):number{
return a + b
}
let result = sum(a:123,b:'123')
== ts 可以编译成任意版本的 js ,可以对编译器进行配置,来决定编译成哪种版本,后面会学习到==
要是想演示一下看效果,可以创建一个index.html文件,将js文件引入到html文件里面,运行html文件即可。
ts 代码改完了记得进行编译:
tsc 文件名
。
小结:
let 变量 : 类型
let 变量 : 类型 = 值;
function fn(参数 : 类型 , 参数 : 类型) : 类型{ … }
2. 类型大全
类型 | 描述 |
---|---|
number | 任意数字 |
string | 任意字符串 |
boolean | 布尔值 |
字面量 | 限制变量的值就是该字面量的值 |
any | 任意类型 |
unknown | 类型安全的any |
void | 没有值或undefined |
never | 不能是任何值 |
object | 任意的 JS 对象 |
array | 任意 JS 数组 |
tuple | 元素,TS 新增类型,固定长度的数组 |
enum | 枚举,TS 中新增类型 |
2.1 字面量
可以使用字面量进行类型声明
let a:number = 10; //原写法
let a:10; //使用字面量进行类型声明,说明a是number类型,但是这时a的值就固定了
a = 11; //报错
一般这样用:
联合类型常用
// |表示或,用来连接多个类型(联合类型)
let b:string | boolean
b = true //不报错
b = 'hello' //不报错
let a: 10 | 11
a = 10 //不报错
a = 11 //不报错
a = 12 //报错
2.2 any / unknown
表示任意类型,设置any
类型后对该变量关闭类型检测,不建议使用any
声明变量不指定类型,则TS解析器自动判断变量的类型为any
(隐式any
)
let c:any;
//均不报错
c = false
c = 'hello'
c = 123
如果实在不知道变量d的类型,那我们可以使用unknown
。
unknown
实际上是一个类型安全的any。
//unknown 表示未知类型
//均不报错
let d : unknown;
d = 123
d = 'hello'
d = true
=c 的类型是any,可以赋值给任意变量;unknow
类型的变量d赋值给其他类型的变量会报错=
s = 'ruru'
s = c; //不报错
s = d; //报错,这种情况可以处理
处理办法:
- 判断
if (typeof d === 'string') {
s = d;
}
- 类型断言
/*
*语法:
* 变量 as 类型
* <类型>变量
**/
s = d as string;
s = <string>d;
2.3 void / never
设置函数返回值用的多
void
表示为空
没有返回值,类型设置为void
,没写类型默认是void
function fn():void{
}
never
表示永远不会返回结果
function fn2():never{
throw new Error('报错了')
}
2.4 object
在js中一切皆对象
//object表示一个对象,这种写法不常用
let a: object;
a = {};
a = function () { };
语法:{属性名:属性值 , 属性名:属性值}
{}指定对象中可以包含哪些属性
//这种常用
//?表示可选属性
let b: { name: string, age?: number };
b = { name: '孙' }
b = { name: '孙', age: 18 }
优化一下:
//?表示可选属性
//[propName: string]: any 表示任意类型的属性
let b: { name: string, [propName: string]: any };
b = { name: '孙' }
b = { name: '孙', age: 18, a: 1, b: 2 }
函数:
设置函数结构的类型声明
语法:(形参:类型,形参:类型) => 返回值
//没啥意义,我们是希望来限制它的结构
let f:Function
这样写:希望函数f的参数类型都是number,返回值也是number
let f:(a:number,b:number) => number
f = function(n1,n2){
return n1 + n2
}
语法学习无捷径,多写多做是良训。
2.5 array
开发中数组中存储一种类型的值。
语法:类型[ ] or Array<类型>
let e: string[];
let x: Array<string>;
let z: number[];
e = ['a','b',1] //报错,因为里面有数字和字符串两种类型
x = ['1','2','3']
z = [1,2,3]
2.6 tuple
元组类型,TS新增,固定长度的数组
语法:[类型,类型,…]
let v: [string, string];
v = ['h', 'h']
v = ['a','a','a'] //报错
2.7 enum
枚举,TS新增,把可能情况都列出来
enum Gender {
//定义一个枚举的类
Male,
Female
}
let i: { name: string, age: number, gender: number }
i = {
name: '孙悟空',
age: 500,
gender: 1
}
console.log(i.gender = Gender.Male)
2.8 其它
- &与
let t = {name:String}& {age:number} //这代表对象t要同时有这两种属性
- 类型的别名
给1 | 2 | 3 | 4
起个别名,很方便,简化类型的使用。
type myType = 1 | 2 | 3 | 4
let a: myType;
let b: myType;
let c : myType;
四、编译选项
1. 监视
TS写完之后需要编译才能执行。
新建一个TS文件,我们编写一段代码,运行 tsc 文件名
进行编译。
每当修改ts文件时都要一遍遍重新编译,忒麻烦,我们使用监视器监视我们这段代码,每当代码发生变化时就重新编译代码,不需要手动操作了。
有个时间间隔,需要等待一下
tsc 文件名 -w
只针对于单一文件,需要每个文件都配置。不太适合日常开发。ctrl c
退出监视。
2. tsconfig.json
在目录下创建一个tsconfig.json
文件,对tsc
进行配置.
这时命令行输入 tsc
就可以编译文件里所有的TS文件了。
tsc -w
监视的是所有的TS文件
tsconfig.json
是ts编译器的配置文件,ts编译器可以根据它的信息来对代码进行编译。
3. 配置选项
3.1 include / exclude
这两个直接写在大括号里
include
:用来指定哪些ts文件需要被编译
"include": [
// 表示src目录下的任意文件夹下的任意文件都能被执行
"./src/**/*"
]
exclude
:指定不需要编译的,默认值"node_modules","bower_components","jspm_packages"
"exclude": [
//表示src目录下的app.ts不需要被编译
"./src/app2.ts"
]
3.2 extends files
这两个直接写在大括号里
extends
:定义被继承的配置文件
//当前配置文件会自动包含config目录下base.json中的所有配置信息
"extends":"./config/base"
files
:指定被编译文件的列表,文件少可能才会用到
"files":[
"app.ts",
"app2.ts",
...
]
3.3 CompilerOptions【重要】
编译器选项,稍微麻烦一些,且看下面详解
3.3.1 target
指定TS被编译为的ES版本,默认ES3版本
可选:'ES3' , 'ES5' , 'ES2016' , 'ES2017' , 'ES2018' , 'ES2019' , 'ES2020'
等
例如转成ES6版本,esnext代表的是ES最新版本
"CompilerOptions": {
"target": "es6"
}
3.3.2 module
指定使用的模块化的规范
可选:none,commonjs,and,system,umd,es6
等
"CompilerOptions": {
"target": "ES6",
"module": "commonjs"
}
3.3.3 lib
指定项目中用到的库,一般情况不需要改。
只要是在浏览器中运行一般不需要管,在node.js中执行根据实际情况修改。
"CompilerOptions": {
"target": "ES6",
"module": "commonjs",
//"lib": [
// "dom"
// ]
}
3.3.4 outDir outFile
outDir
:指定编译后文件所在目录
outFile
:代码合并为一个文件, 全局作用域中的代码会合并到同一个文件中,用的不多,了解一下。
"CompilerOptions": {
"target": "ES6",
"module": "commonjs",
// "lib": [
// "dom"
//],
"outDir": "./dist",
"outFile": "./dist/app.js"
}
3.3.5 allowJs checkJs
checkJs
:是否对js文件进行编译,默认是false
checkJs
:检查js代码是否符合js代码规范,默认值false
"allowJs": true,
"checkJs": true
3.3.5 removeComments noEmit noEmitError
removeComments
: 是否移除注释
"removeComments":true
noEmit
: 不生成编译后的文件
"noEmit":true
noEmitError
有错误时不生成编译后的文件
"noEmitError": true
3.3.6 alawaysStrict noImplicitAny noImplicitThis
alawaysStrict
: 指定编译后的文件是否使用严格模式
"alwaysStrict":true
noImplicitAny
: 不允许隐式的any类型
"noImplicitAny": true
noImplicitThis
: 不允许不明确类型的this
"noImplicitThis":false
3.3.7 strictNullChecks strict
strictNullChecks
:严格检查空值
"strictNullChecks":true
box1不存在也没有报错,当设置了strictNullChecks
时,会报错“box1可能为空”
let box1 = document.getElementById('box1');
box1.addEventListener('click', function () {
alert('hello');
})
解决办法:可以使用if
判断是否为空。
strict
:总开关,开发建议设为true
综上
以上是比较常用的,其他的用到现查。
五、webpack打包ts代码
1.初始化项目:
生成一个package.json
npm init -y
2. 下载构建工具
npm i -D webpack webpack-cli ts-loader typescript
3.创建webpack.config.js
并编写
//引入一个包
const path = require('path');
//webpack配置信息都写在module.exports里面
module.exports = {
//指定入口文件,在文件中新添加一个index.ts
entry: './src/index.ts',
//指定打包文件
output: {
path: path.resolve(__dirname, "dist"),
//打包好文件的文件
filename: 'bundle.js'
},
//指定webpack打包时要使用的模块
module: {
//指定要加载的规则
rules: [
{
//test:规则生效的文件
//用ts-loader处理ts结尾的文件
test: /\.ts$/,
use: 'ts-loader', //要使用的loader
exclude: /node-modules/ //要排除的文件
}
]
}
}
4. tsconfig.json文件配置
{
"CompilerOptions": {
"module": "ES2015",
"target": "ES2015",
"strict": true
}
}
打包,在package.json
中添加一行命令"build":"webpack
执行webpack
,对文件进行打包
npm run build
出现一个dist文件夹,就成功了!
5. 补充
自动生成html文件,自动引入js文件:
安装插件:
npm i -D html-webpack-plugin //帮助我们自动生成html文件
webpack.config.js
里引入并编写代码
const HTMLWebpackPlugin = require('html-webpack-plugin')
//配置webpack插件,
plugins: [
new HTMLWebpackPlugin({
// title:'ruru' //自定义title
template:"文件地址" //写好一个文件,作为生成html文件的模板
});
]
在浏览器中访问网页,自动刷新
安装插件
npm i -D webpack-dev-server
package.json配置:"start": "webpack serve --open chrome.exe"
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1",
"build": "webpack",
"start": "webpack serve --open chrome.exe" //新添的
},
启动项目成功会自动打开浏览器。可以实时更新
编译前dist目录清空
安装插件
npm i -D clean-webpack-plugin
和那个html用法一样
const {CleanWebpackPlugin} = require('clean-webpack-plugin')
//配置webpack插件,
plugins: [
new CleanWebpackPlugin(),
new HTMLWebpackPlugin({
// title:'ruru' //自定义title
template:"文件地址" //写好一个文件,作为生成html文件的模板
});
]
告诉webpack哪些模块可以被引入
在webpack.config.js
中设置
//用来设置引用模块
resolve: {
extensions: ['.ts', '.js']
}
}
让代码有更好的兼容性
安装babel插件
npm i -D @babel/core @babel/preset-env @babel-loader core-js
在package.json
中查看是否成功安装成功。
修改webpack.config.js文件
//指定webpack打包时要使用的模块
module: {
//指定要加载的规则
rules: [
{
//test:规则生效的文件
//用ts-loader处理ts结尾的文件
test: /\.ts$/,
use: [
{
loader: 'babel-loader', //指定加载器
//配置babel
options: {
//设置预定义的环境
presets: [
//指定环境插件
"@babel/preset-env",
//配置信息
{
//要兼容的目标浏览器
targets: {
"chrome": "88"
},
//指定corejs的版本
"corejs": "3",
//使用corejs方式使用usage表示按需加载
"useBuiltIns": "usage"
}
]
}
},
'ts-loader'
], //要使用的loader
exclude: /node-modules/ //要排除的文件
}
]
},
六、面向对象
程序之中所有操作都要通过对象来完成。
操作浏览器要使用window对象,操作网页使用document对象,操作控制台使用console对象。
对象包含属性和方法。
1. 类class
类是对象的模型,我们通过类创建对象。比如说Car类可以创建汽车的对象。
定义类:
// 使用class关键字定义类
class Person {
name: string = 'ruru' //实例属性
age: number = 18
static sex:string = "女"
readonly anim:string = "shushu" //只读
sayHello(){ //定义方法
console.log('hello!!')
}
}
//使用类创建一个对象
const p1 = new Person();
直接定义的属性是实例属性,通过实例访问。
在类中属性前使用static
关键字可以定义类属性(静态属性),可以通过类访问
readonly
开头的属性只读,不可更改。
方法和类在上述三点是相通的
2. 构造函数和this
class Dog {
name: string
age:number
// 构造函数在对象创建时调用
//再实例方法中,this表示的是当前的实例
constructor(name: string, age: number) {
this.name = name
this.age = age
console.log(this)
}
}
const d1 = new Dog('wang', 1);
const d2 = new Dog('bai', 2)
这样就可以传入不同参数获得不同的对象
通过this表示当前调用方法的对象。
3. 继承
Dog类
class Dog {
name: string
age:number
constructor(name: string, age: number) {
this.name = name
this.age = age
}
sayHello(){
console.log('汪汪汪!')
}
}
const d1 = new Dog('xiaohei', 2)
d1.sayHello()
Cat类
class Cat {
name: string
age: number
constructor(name: string, age: number) {
this.name = name
this.age = age
}
sayHello() {
console.log('喵喵喵~')
}
}
const c1 = new Cat('xiaobai', 1)
c1.sayHello()
这两个类的结构很相似,代码很冗余。
代码提取出来,然后共享
定义一个Animal
类
class Animal {
name: string
age: number
constructor(name: string, age: number) {
this.name = name
this.age = age
}
sayHello() {
console.log('喵喵喵~')
}
}
class Dog extends Animal { //继承
run(){
console.log('旺财在跑')
}
}
class Cat extends Animal { //继承
sayHello(){
console.log('喵喵喵') //覆盖掉父类的方法,这种叫做方法的重写
}
}
使用继承后,子类将拥有父类所有的方法和属性。
通过继承可以将多个类中共有的代码写在一个父类中,避免代码的重复。
可以直接在子类中添加你想要添加属性和方法,可以正常使用,参考Dog
里的run
方法。
继承中的super
,表示当前类的父类
class Dog extends Animal {
sayHello() {
super.sayHello();
}
}
注意:如果在子类写了构造函数,此时在子类的构造函数中必须对父类的构造函数进行构造。
4. 抽象类
禁止一个类来创建对象
以abstract
开头的类是抽象类,不能创建对象只能作为父类用来继承,没有其它作用。
抽象类中可以添加抽象方法,使用abstract
开头,只能定义在抽象类中,子类必须对抽象方法进行重写。
abstract class Animal{
name:string
constructor(name:string){
this.name = name
}
abstract sayHello(){ //在子类中必须被重写哦
//console.log('动物在叫!')
}
}
5. 接口
接口用于定义一个类结构,interface
开头
接口用来定义一个类中应该包含哪些属性和方法
接口也可以当成类型声明去使用
接口可以在定义类的时候去限制类的结构,接口中的属性都不能有实际值,只定义对象的结构不考虑实际值,接口中的方法都是抽象方法。
定义类时,可以使类去实现一个接口,实现接口就是使类满足接口的要求。
interface myInterface {
name: string
age: number
}
const obj: myInterface = {
name: 'ruru',
age: 18
}
6. 属性的封装
现在属性是在对象中设置的,属性可以任意的被修改。
class Per {
name: string
age: number
constructor(name: string, age: number) {
this.name = name
this.age = age
}
}
const p2 = new Per('ruru', 20);
数据可随意修改,非常不安全,很危险。
TS 可以在属性前添加属性的修饰符:public
和private
class Per {
_name: string //改成私有属性,外部不能访问
_age: number
constructor(name: string, age: number) {
this._name = name
this._age = age
}
}
记得在tsconfig.json
文件中配置"noEmitOnError":true
通过在类中添加方法,让外部能间接的获取设置这些属性值。
getName(){
return this._name
}
p2.getName()
setName(value:string){
this._name = value
}
p2.setName('猪')
现在选择主动权在我,我可以控制数据是否能被访问,从而有效保证数据的安全。
getter方法用来读取属性
setter方法用来设置属性
- 它们被称为属性的存取器
使用存取器设置name
属性:
get name(){
return this._name
}
set name(value){
this.name = value
}
p2.name = "猪" //直接修改即可
私有属性private
无法在子类中访问
保护属性protect
只能在当前类和子类中访问
7. 泛型
定义函数或者类时类型不明确,执行时才能确定类型
泛型可同时指定多个
function fn<T>(a:T):T{
return a;
}
//指定两个泛型
function fn2<T,K>(a:T,b:K):T{
return a+b;
}
使用:
//直接调用
fn(10);
//指定泛型
fn<string>("hello")
T extends Inter
表示泛型T必须是Inter实现类(子类)
interface Inter{
length:number
}
function f3<T extends Inter>(a:T):number{
return a.length;
}
对象:
class MyClass<T>{
name:T;
constructor(name:T){
this.name = name;
}
}
const mc = new MyClass<String>('孙悟空')
总结
love and peace
希望大家学的开心
欢迎指正