静态类型、类型注解、类型推断、泛型、类型定义文件、模块化、打包编译、装饰器、Metadata、设计模式
一. 定义
TypeScript它是 Javascript 的超集,同时它拥有静态类型。他不能直接通过浏览器环境或者 Node 环境下运行,必须要通过编译器编译成 javascript 代码再运行
// Js
let a = '123'
a = 123 // 因为Js是动态类型,所以此时a可以赋值其他类型的值
// Ts
let a = '123' // 这里的a已经被定为String类型,这是ts自动判定的
a = 123 // 这里的a会提示错误,不允许赋值成数字类型的值
二. 优势
更好的体验,和代码提示
三. 安装
主要使用VSCode,打开设置
- 设置引号:搜索
quote,找到TypeScript的quote配置,选择Single选项,使用单引号 - 设置缩进:搜索
tab,输入2,用两个空格表示 - 设置格式化:安装
Prettier拓展插件 - 设置保存格式化:搜索
Save, 勾选Format On Save,就可以在保存的时候格式化 - 安装
TypeScript:npm install typescript -g - 编译
TS:tsc demo.ts,将会编译成demo.js - 自动编译,需要执行
tsc -w ts-node能直接执行ts代码,不需要先编译ts,再执行js代码
npm install ts-node -g
nodemon监听文件变化并执行
npm install nodemon -D
### package.json
"script": {
"run": "nodemon node ./build/crowller.js"
},
"nodemonConfig": {
"ignore": [
"data/*" #目录/所有文件
]
}
concurrently并行执行node命令
npm install concurrently -D
# package.json
"scripts": {
"dev:build": "tsc -w",
"dev:start": "nodemon node ./build/crowller.js",
# 下面两个相等,第二个是简化写法
"dev": "concurrently npm run dev:build & npm run dev:start"
"dev": "concurrently npm:dev:*"
}
superagent,在node环境调用接口的库cheerio,将html文本进行解析,像jQuery一样调用想要的东西
四. 类型
- 基础类型:
string、number、null、undefined、boolean、symbol、void - 对象类型:
function、object、array、Class
// 数字
let n: number = 1
// 字符串
let s: string = 'string'
// 对象
let temp: number | string = 123
temp = '123'
// 对象
const teacher: {
name: string,
age: number
} = {
name: 'dell',
age: 18
}
interface Person {
name: 'string'
}
const rawData = '{"name": "dell"}'
const newData = JSON.parse(rawData) // 此时newData自动判断的类型是any
const newData2: Person = JSON.parse(rawData) // 指定类型为Person类型
// 数组
const numbers: number[] = [1,2,3]
const arr: (number | string)[] = [1,2,'3']
const arr2: undefined[] = [undefined]
const arr3: [number, string][] = [[1, '1'], [3, '3']]
const arr4: {name: string}[] = [{
name: 'name'
}]
// 元组
const arr5: [string, string, number] = ['string', 'string', 1]
const arr5: [string, string, number][] = [
['string', 'string', 1],
['string', 'string', 1]
]
// 类
class Person {}
const deil: Person = new Person()
// 函数
interface Point {x: number, y: number}
function tsDemo(data: Point) {
return Math.sqrt(data.x ** 2 + data.y ** 2)
}
tsDemo({x: 1, y: 1})
// 函数的入参和返回值类型
const func = (str: string): number => {
return parseInt(str, 10)
}
const func1: (str: string) => number = (str) => {
return parseInt(str, 10)
}
// 函数自己本身的类型
const date: Date = new Date()
五. 类型注解/类型推断
- 如果
TS能够自动分析变量类型,我们就什么也不需要做 - 如果
TS无法分析变量类型的话,我们就需要使用类型注解
// example 1
// getTotal 的两个参数是无法判断是什么类型的,所以需要进行类型注解
// 而getTotal返回的值不需要加注解,因为它会自动推断出返回的是number类型
function getTotal(firstNumber: number, secondNumber: number) {
return firstNumber + secondNumber
}
const total = getTotal(1, 2)
// example 2
// 此例中,如果不小心返回值多写了个 +'' ,那么这个返回类型就
function add(first: number, second: number): number {
return first + second + ''
}
add(1, 2)
函数相关类型
// 函数定义方式
function hello() {}
const hello1 = function() {}
const hello2 = () => {}
// 函数注解
// 函数有返回值,且注解需要的类型
function add(first: number, second: number): number {
return first + second
}
// 函数没有返回值就是Void类型
function sayHello(): void {
console.log('hello')
}
// 如果一个函数永远都不可能执行完,那这个函数的类型就是Never类型
function errorEmitter(): never {
throw new Error()
// 下面这一步永远执行不到
console.log(123)
}
function errorEmitter(): never {
while(true) {}
// 下面这一步永远执行不到
console.log(123)
}
//解构赋值的注解
function add({first, second}: {first: number, second: number}) {
return first + second;
}
const total = add({first: 1, second: 2})
六. Interface 接口
1. Type和Interface
type能代表多种类型interface只能代表一个对象或者函数,不能代表基础类型
interface Person {
name: string
}
type Person = {
name: string
}
type Person1 = string // 而interface不能直接代表string
- 接口对类型的属性定义
interface Person {
readonly name: string, // readonly代表只能读不能写
name: string,
age?: number, // 这个?,代表这个属性可有可无
[propName: string]: any, // 匹配多个其他属性,且属性值为any
say(): string, // 方法,返回值为string
}
// 上面这些属性,name, 和say()方法,是必要的
- 避免强校验
// example
interface Person {
name: string,
age?: number, // 这个?,代表这个属性可有可无
}
const getPersonName = (person: Person): void => {
console.log(person.name)
}
// 这种方式,不会报错
const person = {
name: 'dell',
sex: 'male'
}
getPersonName(person)
// 这种字面量方式会报错,这种会让Typescript进行强校验
getPersonName({
name: 'dell',
sex: 'male'
})
- 接口对类的属性约束
// 下面中的name是类User必须要存在的属性
interface Person {
name: string,
age?: number
}
// 通过implements来对类进行属性约束
class User implements Person {
name: 'dell'
}
- 接口的继承
一个接口可以继承另外一个接口
interface Person {
name: string,
age?: number
}
interface Teacher extends Person {
teach(): string
}
- 定义函数类型
interface SayHi {
(word: string): string // 这个是代表函数的参数,参数的类型和返回值的类型都是string
}
const say: SayHi = (word: string): string => {
return 'string'
}
七. 类的定义和继承
- 类的定义
class Person {
name: 'dell';
getName() {
return this.name;
}
}
const person = new Person();
console.log(person.getName())
- 类的继承
class Teacher extends Person {
getTeacherName() {
return 'dell teacher'
}
}
const teacher = new Teacher()
console.log(teacher.getName())
console.log(teacher.getTeacherName())
super用法,子类可通过super调用父类构造函数,方法和属性
class Teacher extends Person {
getName() {
return super.getName() + 'lee'
}
}
const teacher = new Teacher()
console.log(teacher.getName()) //会打印出 delllee
八. 类的访问和构造器
- 访问类型
// public, private, protected
// public: 允许我在类的内外调用
// private: 允许我在类的内部调用
// protected: 允许我在类的内部调用,以及在继承的子类的内部调用
class Person {
public name: string,
private name2: string,
protected name3: string,
public sayHi() {
console.log(this.name) // 调用成功
console.log(this.name2) // 调用成功
console.log(this.name3) // 调用成功
}
}
class Teacher extends Person {
public sayBye() {
console.log(this.name) // 调用成功
console.log(this.name2) // 调用报错
console.log(this.name3) // 调用成功
}
}
const student = new Person()
console.log(student.name) // 调用成功
console.log(student.name2) // 调用失败
console.log(student.name3) // 调用失败
- 构造器
Constructor
- 写法
// 下面的两种类的声明其作用是一致的
// 传统写法
class Person {
public name: string;
constructor(name: string) {
this.name = name
}
}
// 简化写法,直接简化了变量的声明和赋值
class Person {
constructor(public name: string) {}
}
// 不管上面哪种写法,都可以正常调用
const person = new Person('dell')
console.log(person.name)
- 继承
如果父类有构造器,子类也有构造器,那么子类必须要在构造器中,调用父类的构造器
class Person {
constructor(public name: string) {}
}
class Teacher extends Person {
constructor(public age: number) {
// super() // 这一步必须要写,如果子类继承父类,必须要调用super(),执行父类的构造函数
super('dell') // 如果父类需要传参数,则super中也需要传参数
}
}
const teacher = new Teacher(28)
console.log(teacher.age)
九. Getter 和 Setter
class Person {
constructor(private _name: string) {}
get name() {
return this._name + 'lee'
}
set name(name: string) {
const realName = name.split(' ')[0]
this._name = realName
}
}
const person = new Person('dell')
console.log(person.name)
person.name = 'dell lee'
十. 单例模式
主要通过static将方法直接挂载在类上
class Demo {
private static instance: Demo
private constructor(public name: string) {}
public static getInstance() {
if(!this.instance) {
this.instance = new Demo('dell')
}
return this.instance
}
}
const demo1 = new Demo.getInstance(); // 调用后,将会把实例化的Demo存在instance中
const demo2 = new Damo.getInstance() // 从instance中读取
console.log(demo1.name, demo2.name) // dell dell
十一. 抽象类
readonly
class Person {
readonly name: string
}
abstract把通用的东西抽象出来
// 使用abstract定义一个抽象类
abstract class Geom {
abstract getArea(): number // 抽象方法,没有具体实现
width: nubmer
getType() {
return 'Geom'
}
}
// 下面三个是抽象类的实现
class Circle extends Geom {
getArea() { // 具体实现
return 123
}
}
class Square extends Geom {}
class Triangle extends Geom {}
十二. .d.ts 翻译文件的使用
由于typescript的出现,很多之前使用javascript编写的库文件,无法应用在ts上,所以官方给出了解决方案:使用.d.ts这个翻译文件。
使用方法:去 npm 官网或者 github 搜索对应的翻译文件@type/packageName,然后进行安装