1. TypeScript介绍
Typescript是由微软公司开发的用于扩展JavaScript语言的一种语言。目前浏览器不能直接解析typescript,需要将TS转变为JS文件才可以,TS可以被编译为任意版本的JS。
2. TS的开发环境搭建
- 下载nodeJS,nodeJS推荐使用nvm进行管理。
- 使用npm全局安装typescript
npm i -g typescript - 使用
tsc将TS文件编译为JS文件,常用的语法为:tsc -w test.ts,此时在对应的目录下会新建编译后的JS文件,-w用来监听源文件的变化,实时进行编译。
3. TS基础知识
3.1 基本类型
TS可以在声明变量时为变量指明对应的类型。以下四种方式均可以让TS知道声明的变量对应的数据类型,从而进行限制。
类型声明时也可以使用|进行连接,其表示变量可以被赋予多种数据类型。
let name:string;
let name:string='zahngsan';
// 变量声明和赋值同时进行,TS可自动对变量进行类型检查
let name="123"
类型声明通常用于函数形参以及函数返回值。
function(name:string,age:number):number{
}
我们可以指定的类型包括:
- number
- string
- boolean
- 字面量
- any,any表示任意类型,相当于关闭该变量的TS类型检测
- unknown,表示未知类型。unknown类型的变量不能赋值给其他变量。
- 如果想要为其他变量赋值的话,常用的有两种解决方法:
- 断言,使用
变量 as type - 使用
<type>变量
- 断言,使用
- 如果想要为其他变量赋值的话,常用的有两种解决方法:
- void,用于函数中,表示函数无返回值。
- never,用于函数表示永远不会返回结果
- object,表示返回对象。一般很少使用,可以直接使用
{}来限定对象中的属性。如果在声明的过程中设置了若干属性,在对象赋值的时候如果缺少某一属性值,会发生报错。此时可通过以下方式将对应的属性设置为可选属性。
let b = {name:string, age?:number}
除此之外,如果在对象中有很多属性不确定的情况,也可以使用[propName:string]:any来声明任意类型的属性。
let obj:{name:string,[propName:string]:any};
- Function,Function其实也和Object无很大差别。如果我们想要设置函数结构的类型,可以使用类似箭头函数的结构来实现。
let fun =(a:number, b:bumber)=>number
- array,array的话,TS我们通常用来存储相同类型的内容。数组有两种表示方式。
//表示定义字符串类型的数组
(1)let arr:string[];
(2)let arr1:Array<number>;
- tuple,元组。即固定长度的数组。
// 声明固定长度的内容
let h:[string,string]
- enum,枚举。枚举通常用来存储一些固定的值,比如性别、状态等。可以将所有可能的情况列出来,开发人员可以直接观察到变量表示的意思,而不用记忆变量不同值代表的不同含义。
enum Gender={
Male=0,
Female=1
}
let student:{name:string,gender:Gender};
student={
name:'xiaoli',
gender:Gender.Male
}
3.2 TS编译选项
tsc origin.tsx -w,可以监听对应TS文件的变化,实时编译为JS文件- 上述指令只能对单个文件进行编译。如果想要监听所有TS文件变化并进行编译,需要在项目的tsconfig.json中进行配置。当项目下创建了该文件以后,使用
tsc就可以监听所有TS文件的编译情况了。
tsconfig.json中可以写注释,是ts编译器的配置文件。
下面介绍tsconfig.json支持的配置项:
-
include
include表示项目中的哪些TS文件会被编译器编译。 -
exclude
exclude,表示这些路径下的TS文件不会被编译成JS。 -
extends
extends定义被继承的配置文件,即我们当前的json文件会继承其他文件中的配置选项。 -
files
files表示需要编译哪些文件。 -
compilerOptions,编译器的选项。
- target:表示JS被编译的版本。默认值是ES3,可支持ES6、ES 2016....
- module:指定要使用的模块化规范。包括ES6。
- lib:指定项目中要使用的库。一般不需要改动
- outDir:编译得到的JS文件的输出目录。一般为‘/dist’
- outFile:可以将编译后得到的JS合并到一个文件中。
- allowJs:是否对JS文件进行编译,默认是false
- checkJs:是否检查JS代码
- removeComments:是否移除注释
- noEmit:不生成编译后的文件
- noEmitOnError:当TS代码中有错误时,不编译为JS文件
- 语法相关的属性
- alwaysStrict:用来设置编译后的文件是否使用严格模式。默认是false。JS严格模式会在文件头部添加"use strict",当启动模块功能时,则默认进入严格模式
- noImplicitAny:检查是否不允许隐式的any
- noImplicitThis:检索是否不允许类型不明确的this
- strictNullCheck:严格的检查空值。boolean类型
- strict:所有严格检查的总控制,一般设置为
true
{
"include":[
//**表示任意目录,*表示任意文件
"./src/**/*"
],
"compilerOptions":{
"target":"ES6",
"module":" ES6",
"lib":["XX"],
"outDir":"./dist",
"outFile":"./dist/app.js"
}
}
4. 面向对象
对象是我们对现实中的事物进行抽象的。一个事物处于程序的世界的时候,那么就是以对象的事物去存在的。我们要操作某一个事物,首先得拿到这个对象,去完成这个对象上的行为【也就是api】,从而完成某一操作。 对象通常由属性和方法组成。
4.1 类(class)
类可以理解为对象的模型,类是将对象中的一些共有的属性或者方法抽象出来。其中构造函数是我们向外部提供的接口,可以将外部传递的值存入到类的内部。
class 类名
{
// 定义实例属性
属性名:类型;
// 静态属性定义类属性[不需要new对象就可以使用的属性]
static age:number;
constructor(参数:类型){
this.属性名=参数
}
方法名(){
}
}
属性包括两种方式:
- 实例属性
直接在类中定义的属性属于实例属性,需要将对象new,出来然后在对象上访问才能生效。
- 静态属性
静态属性使用static修饰,不需要创建对象,可以直接Student.name直接访问到内容。
方法的话直接使用
方法名(){}进行定义。
4.2 构造函数和this
构造函数为创建对象提供可多样化,每次new一个class类,都能获得属性不同的对象。
构造函数使用constructor表示,会在new 的时候执行。[这里涉及到new时发生了什么?]
class Person {
name: string;
age: number;
constructor(name: string, age: number) {
// 在构造函数中,this就是我们当前新建的那个对象
this.name = name;
this.age = age;
console.log('构造函数被执行了')
}
}
let p1 = new Person('xiaozhag', 14);
let p2 = new Person('frfr', 15);
console.log(p1)
console.log(p2)
this表示当前对象.
4.3 继承
4.3.1 基础概念
继承就是将一些共有的内容提取出来。后续其他类可通过extends关键字将公共部分的内容存放到自身中。继承衍生出来两个内容:父类和子类。下面的代码中Person称为父类,Student称为子类。子类可以获取父类所有的方法和属性。
class Person {
name: string;
age: number;
constructor(name: string, age: number) {
// TODO 在构造函数中,this就是我们当前新建的那个对象
this.name = name;
this.age = age;
console.log('构造函数被执行了')
}
run() {
console.log(`${this.name}跑了`)
}
}
class Student extends Person {}
let p1 = new Student('张老师', 35);
console.log(p1)
p1.run();
如果父类中的方法不能完全满足子类功能,则可以重写父类中的方法,让其仅在当前子类生效。
注意:在实际项目开发过程中,尽量不要改动之前声明的类型。如果已有类无法满足我们的实际需求,可以新创建一个类去继承原有类型,在新类中去做改动。
4.3.2 super
super我们理解为就是父类的含义。通常情况下,在子类中如果不写constructor构造函数,默认我们在创建子类对象时,就已经调用了父类的构造函数。
但是如果在子类中我们直接写constructor构造函数,则会产生Must call super constructor in derived class before accessing 'this' or returning from derived constructor错误,此时调用super,才能保证继承成功。
4.3.3 抽象类
抽象类是一个专门用来被继承的类型。使用abstract来声明,定义的抽象类不能创建实例对象。抽象类中可以用来声明抽象方法,抽象方法中只定义方法的结构体,而不具体实现方法。
子类在继承父类的时候,必须重新抽象方法,否则会产生错误。
abstract class Person {
name: string;
constructor(name: string) {
this.name = name;
}
// 子类继承方法时,必须对该方法进行重写。
abstract sayHello(): void;
}
// 不能进行创建,会报错无法创建抽象类的实例。
let person = new Person('xiaoming');
4.5 接口
上述中我们定义了抽象类,抽象类我们可以理解为约定了一些规范内容,需要后代去按照这个规范去做事情。同样,我们可以使用接口来完成这样一件时间。
接口是用来定义一个类的结构。关于接口需要注意几个点:
- 接口在同一作用域内可以重复声明,但是
type不可以 - 接口可以在定义类的时候去限制类的类型
- 接口不能有实际值,只用来定义对象的结构
- 接口中所有方法都是抽象方法
- 实现接口使用关键字
implements,实现接口必须把接口所有属性和方法全部都实现。
// 普通类型声明
type myType = {
name?: string;
age?: number;
sex?: string;
}
/**
* 类型声明
*/
interface muInterface {
name?: string;
age?: number;
sex?: string;
}
// 在声明一些属性时,如果不确定是否一定存在,可以使用可选属性来定义
const obj: myType={
name: '123',
age: 12,
}
4.6 泛型
在遇到类型无法确定的情况下,我们可以使用泛型。
function Person<T>(name: T) {
console.log(name)
}
Person<number>(1)//指定T的类型是number
Person('zhangsan')//会自动匹配类型
- 可以直接调用具有泛型的函数
- 不指定泛型,TS可以自动对类型进行推断
- 指定泛型,指定什么类型就只能赋值对应类型的变量
- 泛型可以同时指定多个
- T extends Inter 表示泛型T必须是Inter的实现类。