一、简介
- TypeScript是JavaScript的超集
- TS代码需要通过编译器编译成JS,然后再交由JS解析器执行
- TS完全兼容JS
- 优点
- 相较于JS而言,TS拥有了静态类型,更加严格的语法,更强大的功能;
- TS可以在代码执行前就完成代码的检查,减小了运行时异常的出现的几率;
- TS代码可以编译为任意版本的JS代码,可有效解决不同JS运行环境的兼容问题;
- 同样的功能,TS的代码量要大于JS,但由于TS的代码结构更加清晰,变量类型更加明确,在后期代码的维护中TS却远远胜于JS
二、基本类型
1、类型声明
- 通过类型声明可以指定TS中变量(参数、形参)的类型
- 指定类型后,当为变量赋值时,TS编译器会自动检查值是否符合类型声明,符合则赋值,否则报错
- 语法
let 变量: 类型;
let 变量: 类型 = 值;
// 类型3是指定返回值类型
function fn(参数: 类型, 参数: 类型): 类型3{
...
}
2、类型
类型 | 例子 | 描述 |
---|---|---|
number | 1, -33, 2.5 | 任意数字 |
string | 'hi', "hi", hi | 任意字符串 |
boolean | true、false | 布尔值true或false |
字面量 | 其本身 | 限制变量的值就是该字面量的值 |
any | * | 任意类型 |
unknown | * | 类型安全的any |
void | 空值(undefined) | 没有值(或undefined) |
never | 没有值 | 不能是任何值 |
object | {name:'孙悟空'} | 任意的JS对象 |
array | [1,2,3] | 任意JS数组 |
tuple | [4,5] | 元素,TS新增类型,固定长度数组 |
enum | enum{A, B} | 枚举,TS中新增类型 |
number,string,boolean
let decimal: number = 6;
let isDone: boolean = false;
let color: string = "blue";
color = 'red';
字面量
let color: 'red' | 'blue' | 'black';
any
any表示任何类型,相当于关闭了TS的类型检测(会祸害别人)
let d: any = 4;
d = 'hello';
d = true;
unknown
表示未知类型的值,实际上就是一个类型安全的any
unknow类型的值,不能直接赋值给其他变量
不知道类型的值,最好用unknown
let e:unknown;
e='sss';
let f:string
f=e; // 会报错
void
let unusable: void = undefined;
never
# 表示没有返回值
function error(message: string): never {
throw new Error(message);
}
object
let l: object;
l = {};
# 在属性后面加上?,表示属性是可选的
// age属性可有可无
let aa: { name: string, age?: number };
# [propName:string]:unknow 表示任意类型的属性
let bb: { name: string, [propName: string]: unknown }
bb = { name: 'lyw', age: 12, sex: '男' }
array
let list: 类型[]
let list: Array<类型>
tuple
let x: [string, number];
x = ["hello", 10];
enum
enum Gender {
Male = 0,
Female = 1
}
let i: { name: string, gender: Gender };
i = {
name: 'lyw',
gender: Gender.Male
}
3、类型断言
- 类型断言,可以用来告诉解析器变量的实际类
# 语法
1.变量 as 类型
2.<类型>变量
let someValue: unknown = "this is a string";
let strLength: number = (someValue as string).length;
4、类型检测
- typeof:可以用来获取一个变量或对象的类型
interface Hero {
name: string;
skill: string;
}
const zed: Hero = { name: "yyqx", skill: "sing" };
type LOL = typeof zed; // 等于 type LOL = Hero
- instanceof
class NumberObj {
count: number;
}
function addObj(first: object | NumberObj, second: object | NumberObj) {
if (first instanceof NumberObj && second instanceof NumberObj) {
return first.count + second.count;
}
return 0;
}
- keyof:与Object.keys略有相似,只不过keyof取interface的键
interface Point {
x: number;
y: number;
}
// type keys = "x" | "y"
type keys = keyof Point;
三、编译选项
1、自动编译文件
- 编译文件时,使用 -w 指令后,TS编译器会自动监视文件的变化,并在文件发生变化时对文件进行重新编译。
tsc xxx.ts -w
2、自动编译整个项目
- 需要先在项目根目录下创建一个ts的配置文件
tsconfig.json
- 添加配置文件后,只需tsc命令即可完成对整个项目的编译
tsconfig.json:是ts编译器的配置文件,ts编译器可以根据它的信息来对代码进行编译
- 配置选项
- include:指定需要被编译的ts文件所在目录
# 表示src目录下的所有ts文件都会被编译
# **表示任意目录,*表示任意文件
"include":["./src/**/*"]
- exclude:指定不需要被编译的ts文件所在目录
# 默认值:["node_modules", "bower_components", "jspm_packages"]
# 表示scr/hello目录下的所有ts文件不会被编译
"exclude":["./src/hello/**/*"]
- extends:被继承的配置文件
# 表示:当前配置文件中会自动包含configs目录下base.json中的所有配置信息
"extends":"./configs/base"
- files:指定被编译文件的列表(需要被编译文件少时才会用到)
# 以下文件都会被TS编译器所编译
"files": [
"core.ts",
"sys.ts",
"types.ts",
"scanner.ts"
]
compilerOptions
-
编译选项是配置文件中非常重要也比较复杂的配置选项
-
常规选项
- target:用来指定ts被编译为的ES的版本
# 可以为es3、es6、es2015、es2016、es2017 // es6版本 "compilerOptions": { "target": "ES6" }
- module:指定要使用的模块化的规范
# 常用:'none', 'commonjs',es6','system' "module":"es2015" // es6模块化
- lib:指定项目中要使用的库
# 一般不会修改,可以选择的很多 # 可选值:ES5、ES6/ES2015、ES7/ES2016、DOM、ESNext "lib":["dom"]
- outDir:指定编译后的js文件所在的目录
"outDir":"./dist"
- outFile:将所有文件编译为一个js文件
# 默认会将所有的编写在全局作用域中的代码合并为一个js文件,如果module制定了None、System或AMD则会将模块一起合并到文件之中 "outFile":"./dist/app.js"
- rootDir:指定代码的根目录
# 指定编译后的文件所放目录 "rootDir": "./src"
- allowJs:是否对js文件进行编译,默认是false
"allowJs":false
- checkJs:是否检查js代码是否符合语法规范,默认是false
"checkJs":false
- removeComments:是否移除注释
"removeComments":true
- noEmit:不生成编译后的文件
"noEmit":false
- noEmitOnError:当存在错误时,不生成编译后的文件
"noEmitOnError":true
-
检查类属性
- strict:所有严格检查的总开关,默认值是true
- alwaysStrict:是否总是以严格模式对代码进行编译
- noImplicitAny:不允许隐式的any类型
- noImplicitThis:不允许类型不明确的this
- strictNullChecks:严格的检查空值
- noImplicitReturns:检查函数没有隐式的返回值
- strictFunctionTypes:严格检查函数的类型
- strictPropertyInitialization:严格检查属性是否初始化
- noUnusedLocals:检查未使用的局部变量
- noUnusedParameters:检查未使用的参数
- strictBindCallApply:严格检查bind、call和apply的参数列表
- noFallthroughCasesInSwitch:检查switch语句包含正确的break
四、使用webpack打包ts代码
- 通常情况下,实际开发中我们都需要使用构建工具对代码进行打包,TS同样也可以结合构建工具一起使用,下边以webpack为例介绍一下如何结合构建工具使用TS。 步骤
- 初始化:通过cnpm init -y:生成package.json文件
- 安装:
npm i -D webpack webpack-cli webpack-dev-server typescript ts-loader clean-webpack-plugin
// webpack:构建工具
// webpack-cli:wenpack的命令行工具
// webpack-dev-server:webpack的开发服务器
// typescript:ts编译器
// ts-loader:ts加载器,用于在webpack中编译ts文件
// html-webpack-plugin:自动创建html文件
// clean-webpack-plugin:清除插件,每次构建都会先清除目录
- 配置webpack.config.js文件
// 引入包
const path = require('path
const HtmlWebpackPlugin = require("html-webpack-plugin");
const { CleanWebpackPlugin } = require("clean-webpack-plugin");
// webpack中所有的配置信息都应该卸载module.exports中
module.exports = {
optimization:{
minimize: false // 关闭代码压缩,可选
},
devtool: "inline-source-map",
devServer: {
contentBase: './dist'
},
// 指定入口文件
entry: './src/index.ts',
// 指定打包文件所在目录
output: {
path: path.resolve(__dirname, 'dist'), // 指定打包文件的目录
filename: "bundle.js",
environment:{
arrowFunction:false // 告诉webpack不使用箭头函数
}
},
// 指定webpack打包时要使用的模块
module: {
rules: [
{
test: /\.ts$/, // test指定:规则生效的文件
use: 'ts-loader',
exclude: /node_modules/
}
]
},
// 设置外来插件
// 打包html文件的插件:html-webpack-plugin
plugins: [
new CleanWebpackPlugin(),
new HTMLWebpackPlugin({
title:'这是一个标签', // 设置打包文件的标题
template:'./src/index.html' // 设置打包模板
}),
]
// 用来设置引用模块
resolve: {
extensions: ['.ts', '.js']
},
mode: 'development'
}
- 配置ts编译文件—— tsconfig.js
{
"compilerOptions": {
"module": "es2015",
"target": "es2015",
"strict": true
}
}
- 修改package.json中的build
{
...略...
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1",
// 用webpack打包
"build": "webpack",
# 自动更新操作的结果,打开谷歌浏览器
"start": "webpack serve --open chrome.exe"
},
...略...
}
# 执行
npm run build:对代码进行编译
npm start:启动开发服务器
五、Babel
- 开发中还经常需要结合babel来对代码进行转换以使其可以兼容到更多的浏览器
- 在上述步骤的基础上,通过以下步骤再将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语法
- 修改webpack.config.js配置文件
...略...
module: {
rules: [
{
test: /\.ts$/,
use: [
// 配置babel
{
// 指定加载器
loader: "babel-loader",
// 设置babel
options: {
// 设置预定义环境
presets: [[
// 指定环境的插件
"@babel/preset-env",
// 配置信息
{
// 要兼容的目标
targets: {
"chrome": "67",
"ie":"11"
},
// 指定corejs的版本
"corejs": "3",
// 使用corejs的方式,“usage”表示按需加载
"useBuiltIns": "usage"
}
]]
}
}
,
'ts-loader'
],
// 要排除的文件
exclude: /node_modules/
}
]
},
...略...
六、对象
1、封装
- 对象实质上就是属性和方法的容器,它的主要作用就是存储属性和方法,这就是所谓的封装
- 默认情况下,对象的属性是可以任意的修改的,为了确保数据的安全性,在TS中可以对属性的权限进行设置
- 只读属性(readonly):如果在声明属性时添加一个readonly,则属性便成了只读属性无法修改
- TS中属性具有三种修饰符:
public(默认值):可以在当前类、子类和对象中修改
protected:可以在当前类、子类中修改
private:只能在类中访问和修改
2、属性存取器
- 设置属性的方法叫做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); // 通过getter读取name属性
p1.name = '猪八戒'; // 通过setter修改name属性
七、类
属性
- 实例属性:通过对象调用
- 静态属性:使用static开头,通过类直接调用 方法
- 类方法:如果方法以static开头,则方法就是类方法,可以直接通过类去调用
- 实例方法:通过创建对象,对象调用
# 创建
class Person{
// 实例属性
name:string='lyw';
age:number=16;
// 静态属性
static name='yyqx';
static age=21;
// 类方法
static sayHello(){
console.log('Hello');
}
// 实例方法
sayHello(){
console.log('Hello 11');
}
}
# 调用
const per=new Person();
// 实例属性
console.log(per.name);
// 静态属性
console.log(Person.name);
1、继承extends
- 通过继承可以将其他类中的属性和方法引入到当前类中
- 子类可以扩展父类没有的属性和方法
- 重写:如果在子类中添加了和父类相同的方法,则子类方法会覆盖掉父类的方法
class Animal{
name: string;
age: number;
// 父类构造函数
constructor(name: string, age: number){
// this指向类对象
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();
2、super关键字
- 作用:调用父类的构造函数constructor
- 如果在子类中写了构造函数,则在子类构造函数中必须对调用父类的构造函数
class Animal{
name: string;
constructor(name: string){
// this指向类对象
this.name = name;
}
run(){
console.log(`父类中的run方法!`);
}
}
class Dog extends Animal{
age:number;
constructor(name:string,age:number){
// 调用父类构造函数
super(name);
this.age=age;
}
// 重写
run(){
console.log(`子类中的run方法,会重写父类中的run方法!`);
}
}
const dog = new Dog('旺财', 4);
dog.bark();
3、抽象类abstract
- 作用:抽象类是专门用来被其他类所继承的类,它只能被其他类所继承不能用来创建实例
- 抽象方法
- 以abstract开头,没有方法体
- 只能定义在抽象类中,子类必须对抽象方法进行重写
abstract class Animal{
// 抽象方法
abstract run(): void;
bark(){
console.log('动物在叫~');
}
}
class Dog extends Animals{
run(){
console.log('狗在跑~');
}
}
八、接口(Interface)
- 接口的作用类似于抽象类,它所有的属性和方法都不能有实际的值
- 接口主要负责定义一个类的结构,接口可以去限制一个对象的接口,对象只有包含接口中定义的所有属性和方法时才能匹配接口
- 作用:接口是去定义一个标准,去限制类
interface Person{
name: string;
sayHello():void;
}
function fn(per: Person){
per.sayHello();
}
fn({name:'孙悟空', sayHello() {
console.log(`Hello, 我是 ${this.name}`)}
});
- 定义类时,可以使类去实现一个接口(实现接口就是满足接口的要求)
interface Person{
name: string;
sayHello():void;
}
class Student implements Person{
name:sting;
constructor(name: string) {
this.name=name;
}
sayHello() {
console.log('大家好,我是'+this.name);
}
}
九、泛型(Generic)
- 作用:在定义函数或者类时,遇到类型不明确就可以使用
- 下面的
<T>
就是泛型,T是我们给这个类型起的名字泛型其实很好理解,就表示某个类型
function test<T>(arg: T): T{
return arg;
}
- 怎样使用上边的函数
# 方式一(直接使用)
test(10)
# 方式二(指定类型)
test<number>(10)
- 同时指定多个泛型,泛型间使用逗号隔开
function test<T, K>(a: T, b: K): K{
return b;
}
test<number, string>(10, "hello");
- 类中同样可以使用泛型
class MyClass<T>{
prop: T;
constructor(prop: T){
this.prop = prop;
}
}
- 对泛型的范围进行约束(extends)
Interface Inter{
length:number;
}
function fn3<T extends Inter>(a:T):number{
return a.length;
}
十、 Utility Types
可以理解为基于ts封装的工具类型
Partial<T>
将T中所有的属性转换为可选属性。返回的类型可以是T的任意子集
```
export interface UserModle {
name: string;
age?: number; // 可选属性
sex: number;
}
type JuserModele=Partial<UserModle>
# 例子
let a: JuserModele
a={
name:'sss',
age:12
}
```
Required<T>
通过将T的所有属性设置为必选属性来构造一个新的类型。与Partial相反
```
type JuserModele2=Require<UserModle>
// 例子
let a: JuserModele2
a={
name:'sss',
age:12, // 必须写
sex:'男'
}
```
Readonly<T>
将T中所有属性设置为只读
```
type JUserModel3 = Readonly<UserModel>
// 等价于
type JUserModel3 = {
readonly name: string;
readonly age?: number | undefined;
readonly sex: number;
}
```
Record<K,T>
K对应对应的key,T是对象value的类型,返回的就是一个声明好的对象
```
type TodoProperty = 'title' | 'description';
type Todo = Record<TodoProperty, string>;
// 例子
let a: Todo;
a = {
title: 'sss',
description: 'ddd'
}
```
Pick<T,K>
在一个声明好的对象中,挑选一部分出来组成一个新的声明对象
```
interface Todo {
title: string;
description: string;
done: boolean;
}
type TodoBase = Pick<Todo, "title" | "done">;
// 等价于
type TodoBase = {
title: string;
done: boolean;
}
```
Omit<T,K>
从T中取出除去K的其他所有属性,与Pick相对
Exclude<T,U>
从T中排除可分配给U的属性,剩余的属性构成新的类型
```
type T0 = Exclude<'a' | 'b' | 'c', 'a'>;
// 等价于
type T0 = "b" | "c"
```
NonNullable<T>
去除T中的null和undefined类型
Parameters<T>
返回类型为T的函数的参数类型所组成的数组
```
type T0 = Parameters<() => string>; // []
type T1 = Parameters<(s: string,t:number) => void>; // [string]
```
ReturnType<T>
function T的返回类型
```
type T0 = ReturnType<() => string>; // string
type T1 = ReturnType<(s: string) => void>; // void
```
InstanceType<T>
返回构造函数类型T的实例类型; 相当于js中的,不过返回的是对应的实例
```
class C {
x = 0;
y = 0;
}
type T0 = InstanceType<typeof C>; // C
```