TS 小结 | 青训营

196 阅读5分钟

学习 TS 的原因

对比 ts 和 js

首先 ts 是 js 的超集,可以很好的解决大项目代码的复杂性。同时 ts 是一种强类型语言,因此对于确定类型的变量是不允许随便修改数据类型的,且能够在编译时就发现代码的错误,而 js 可以将变量随意赋值,且只能在运行时发现错误。

ts 的好处

  1. 类型安全: 因为 ts 是一个强类型语言,不能随意修改确定的变量类型
  2. 特性支持好:会支持 ES 新版本里面才支持的功能特性
  3. 工具链完善:因为 ts 带来的强类型,基于这个特性我们更好地理解源码,从而整理开发工具,提高开发效率

TS 基础

基础类型

  • boolean (布尔类型)、 number(整数、复数、浮点数)、string(字符类型)
  • enum(枚举类型):定义数据集合,可以为 数字和字符串,也可以用 const 来修饰成常量

普通枚举:若不设置属性值,则默认值会从 0 开始递增,若设置则为设定值

enum A {
  a, // 0
  b, // 1
  c = 3, // 3
  d, // 4
}
  • any、unknown、void

any 不会被检测,允许任何操作, unknow 只能被赋值而不能被反向赋值

  • never:用于防御性编程,表示永远不存在值的类型
  • [ ]:(数组类型)
  • tuple(元组类型):是数组类型的特殊形式,必须声明数组每一项的类型
let arr: [string, number, boolean] = ['1', 1, true]

函数类型

TS 在定义函数类型时需要定义输入参数的类型和输出类型

输入参数:可以是可选参数和默认参数

输出参数:输出可以自动判断,若没有返回值则为 void 类型

函数重载:对于函数名称相同但是传入参数类型不同的可以支持函数重载

function add(x: number[]): number
function add(x: string[]): string
function add(x: any[]): any {
  if (typeof x[0] === 'string') {
    return x.join()
  }
  if (typeof x[0] === 'number') {
    return x.reduce((acc, cur) => acc + cur)
  }
}

interface

用于定义对象类型,多数情况下用于描述函数类型

interface Test{
  // 只读属性
  readonly id: string
  name: string
  age: number
  hobby: () => string
  // 可选属性
  sex?: boolean
  // 自定义属性
  [propname:string]: string
}

类 Class

相比于 JS 增加了一些定义,如 public、private、protected 修饰符

通过 abstract 申明抽象类,抽象类不能被实例化,方法必须被子类实现

类的继承

使用 extends 关键字实现继承

子类中使用 super 关键字来调用父类的构造函数和方法

class Animal {
  public name: string;
  constructor(name: string) {
    this.name = name;
  }
  say(): string {
    return 'Hello World';
  }
}

class Cat extends Animal {
  constructor(name) {
    // 子类调用父类的构造函数
    super(name); 
  }
  say(): string {
    // 子类调用父类的方法
    return 'Cat, ' + super.say(); 
  }
}

存取器

可以通过 getter、setter 来改变类中属性的读写行为

class Person {  
  name: string  
  constructor(name: string) {    
    this.name = name  
  }  
  get getName() { 
    return this.name  
  }  
  set setName(val: string) {
    this.name = val
  }
}

TS 进阶

高级类型

联合类型 |

let a: number | string

a = 1
a = 'a'

交叉类型 &

下载.png

当交叉类型的属性有重复时,将会导致该属性的类型为 never

  • 类型断言:可以使用 as 关键字实现属性前置的断言定义
  • 类型别名 type

相比于 interface,两者都可以定义对象或者函数,且允许继承

区别是 interface 是 TS 用于定义对象,type 是方便定义别名使用,interface 在重复声明时会合并,而 type 不行

泛型

语法:一般用 T 表示参数类型,写在 <> 中

可以支持多种类型的数据,高度可重用性

function print<T>(arg: T): T {
  console.log(arg);
  return arg
}

print('hello') // hello
print(1) // 1

作用:临时占位,之后通过传入的参数类型来进行自动推导

基本操作符

  • typeof:获取类型
  • keyof: 获取所有键
interface Person {
  name: string
  age: number
}
type k = keyof Person // 'name' | 'age'
  • in:遍历枚举类型
type keys = "a" | "b" | "c"
type Obj = {
  [p in keys]: any
} // => {a: any, b: any, c: any}
  • T[K]:索引访问
interface Person {
  name: string
  age: number
}
let type1: Person['name'] // string
  • extends:泛型约束
interface Length {
  length: number
}
function lengthDefine<T extends Length>(arg: T): T {
  return arg;
}

// 因为定义了 Lengtb 的参数为 length
lengthDefine({length: 5})

工具类型

  • Partial:将类型属性转为可选

    • [P in keyof T] 通过映射类型,遍历 T 上的所有属性
    • ? 设置为属性为可选的
    • T[P] 设置类型为原来的类型
type Partial<T> = {
  [P in keyof T]?: T[P]
}
  • Required:将类型属性转为必选
type Partial<T> = {
  [P in keyof T]-?: T[P]
}
  • Readonly:将类型属性转为只读
type Readonly<T> = {
  readonly [P in keyof T]: T[P]
}
  • Pick: K 中 P 的属性类型设置为 T 中 P 的属性类型
type Pick<T, K extends keyof T> = {
    [P in K]: T[P]
}
  • Record:构造一个 type,key 为联合类型中的每个子类型,类型为 T
type Record<K extends keyof any, T> = {
  [P in K]: T
}

/**
 * @example
 * type Eg2 = {a: B, b: B}
 */
interface A {
  a: string,
  b: number,
}
interface B {
  key1: number,
  key2: string,
}
type Eg2 = Record<keyof A, B>
  • Exclude:构建一个联合类型其类型在 T 中而不包含在 U 中
type Exclude<T, U> = T extends U ? never : T
  • Extract:构建一个联合类型其类型取 T 和 U 的交集
type Extract<T, U> = T extends U ? T : never
  • Omit:从 T 中删除 K 包含的类型
// method 1
type Omit = Pick<T, Exclude<keyof T, K>>

// method 2
type Omit2<T, K extends keyof any> = {
  [P in Exclude<keyof T, K>]: T[P]
}

TS 实战

声明文件

  • declare:三方库需要类型声明文件
  • @types:三方库 TS 类型包
  • .d.ts:声明文件定义
  • tsconfig.json:定义 TS 配置

泛型约束后端接口类型

import axios from 'axios'
interface API {
  '/book/detail': {
    id: number
  }
}
function request<T extends keyof API>(url: T, obj: API[T]) {
  return axios.post(url, obj)
}