笔记-TypeScript

121 阅读7分钟

静态类型类型注解类型推断泛型类型定义文件模块化打包编译装饰器Metadata设计模式

一. 定义

TypeScript它是 Javascript 的超集,同时它拥有静态类型。他不能直接通过浏览器环境或者 Node 环境下运行,必须要通过编译器编译成 javascript 代码再运行

// Js
let a = '123'
a = 123 // 因为Js是动态类型,所以此时a可以赋值其他类型的值
// Ts
let a = '123' // 这里的a已经被定为String类型,这是ts自动判定的
a = 123 // 这里的a会提示错误,不允许赋值成数字类型的值

二. 优势

更好的体验,和代码提示

三. 安装

主要使用VSCode,打开设置

  1. 设置引号:搜索quote,找到TypeScriptquote配置,选择Single选项,使用单引号
  2. 设置缩进:搜索tab,输入2,用两个空格表示
  3. 设置格式化:安装Prettier拓展插件
  4. 设置保存格式化:搜索Save, 勾选Format On Save,就可以在保存的时候格式化
  5. 安装TypeScript: npm install typescript -g
  6. 编译TS: tsc demo.ts,将会编译成demo.js
  7. 自动编译,需要执行tsc -w
  8. ts-node能直接执行ts代码,不需要先编译ts,再执行js代码
npm install ts-node -g
  1. nodemon监听文件变化并执行
npm install nodemon -D

### package.json
"script": {
  "run": "nodemon node ./build/crowller.js"
},
"nodemonConfig": {
  "ignore": [
    "data/*" #目录/所有文件
  ]
}
  1. 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:*"
}
  1. superagent,在 node 环境调用接口的库
  2. cheerio,将 html 文本进行解析,像jQuery一样调用想要的东西

四. 类型

  1. 基础类型:stringnumbernullundefinedbooleansymbolvoid
  2. 对象类型:functionobjectarrayClass
// 数字
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()

五. 类型注解/类型推断

  1. 如果TS能够自动分析变量类型,我们就什么也不需要做
  2. 如果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. TypeInterface

  • type 能代表多种类型
  • interface 只能代表一个对象或者函数,不能代表基础类型
interface Person {
  name: string
}

type Person = {
  name: string
}
type Person1 = string // 而interface不能直接代表string
  1. 接口对类型的属性定义
interface Person {
  readonly name: string, // readonly代表只能读不能写
  name: string,
  age?: number, // 这个?,代表这个属性可有可无
  [propName: string]: any, // 匹配多个其他属性,且属性值为any
  say(): string, // 方法,返回值为string
}
// 上面这些属性,name, 和say()方法,是必要的
  1. 避免强校验
// 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'
})
  1. 接口对类的属性约束
// 下面中的name是类User必须要存在的属性
interface Person {
  name: string,
  age?: number
}
// 通过implements来对类进行属性约束
class User implements Person {
  name: 'dell'
}
  1. 接口的继承

一个接口可以继承另外一个接口

interface Person {
  name: string,
  age?: number
}
interface Teacher extends Person {
  teach(): string
}
  1. 定义函数类型
interface SayHi {
  (word: string): string // 这个是代表函数的参数,参数的类型和返回值的类型都是string
}
const say: SayHi = (word: string): string => {
  return 'string'
}

七. 类的定义和继承

  1. 类的定义
class Person {
  name: 'dell';
  getName() {
    return this.name;
  }
}

const person = new Person();
console.log(person.getName())
  1. 类的继承
class Teacher extends Person {
  getTeacherName() {
    return 'dell teacher'
  }
}
const teacher = new Teacher()
console.log(teacher.getName())
console.log(teacher.getTeacherName())
  1. super 用法,子类可通过 super 调用父类构造函数,方法和属性
class Teacher extends Person {
  getName() {
    return super.getName() + 'lee'
  }
}
const teacher = new Teacher()
console.log(teacher.getName()) //会打印出 delllee

八. 类的访问和构造器

  1. 访问类型
// 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) // 调用失败
  1. 构造器 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

十一. 抽象类

  1. readonly
class Person {
  readonly name: string
}
  1. 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,然后进行安装