TypeScript知识点

167 阅读8分钟

前言: 根据阮一峰博客整理写出的笔记,具体内容可以参考 wangdoc.com/typescript/

  1. 全局安装 npm install typescript -g
  2. 生成配置文件
    tsc --init # ⽣成tsconfig.json tsc可以将ts⽂件编译成js⽂件 tsc --watch监控ts⽂件变化⽣成js⽂件
  3. ts-node可以执行ts文件 安装 npm install ts-node -g
  4. 但是我们一般不采用这种方式,最好集成在webpack,rollup环境中
  5. 配置rollup开发环境
    1. 安装依赖 pnpm install rollup typescript rollup-plugin-typescript2 @rollup/plugin-node-resolve rollup-plugin-serve -D
    2. rollup配置操作 rollup.config.js
import ts from "rollup-plugin-typescript2" // TypeScript 转换器
import serve from "rollup-plugin-serve" // rollupweb服务
import { nodeResolve } from "@rollup/plugin-node-resolve" // 外部依赖
import { dirname, resolve } from "path"
import { fileURLToPath } from "url"
const __filename = fileURLToPath(import.meta.url) // 当前文件的绝对路径
const __dirname = dirname(__filename) // 当前文件所在的文件夹 绝对路径

export default {
  input: resolve(__dirname, "src/index.ts"),
  output: {
    format: "iife",
    file: resolve(__dirname, "dist/bundle.js"),
    sourcemap: true //源码调试功能
  },
  plugins: [
    nodeResolve({
      extensions: [".js", ".ts"]
    }),
    ts({
      tsconfig: resolve(__dirname, "tsconfig.json")
    }),
    serve({
      port: 3000,
      openPage: "/public/index.html",
      open: true
    })
  ]
}
  1. 在packages.json
"type": "module",
"script": {
   dev: "rollup -c -w" 
}
  1. 常用插件 Error Lens--->提示错误插件 ts默认是不执行的
  2. ts类型分类: 内置类型(DOM,Promise,原始方法),基础类型,高级类型,自定义类型
  3. 字符串,布尔,数值,数组,元祖,枚举,null和undefined,void,never,object,Symbol,BigInt,any tip: 元祖和数组的区别? 元祖有固定长度和类型
  4. 类型推导
    // 1)根据赋值来进行推导
    let name = 'zhangsan'
    let ag = 30
    // 2) 根据返回值来进行推导
    type ISum = (x: string, y: string) => string
    let sum: ISum = (a, b) => {
      return a + b
    }
    // 3) 根据上下文来推导,根据位置来推导 void表示不关心返回的具体类型
    type ICallback = (a: string, b: number, c: boolean) => void
    function fn(callback: ICallback) {}
    fn((x, y, z) => {
      return {}
    })
    
  5. 重载: 一般是有限的操作,ts中的重载是伪重载(类型的重载,而不是逻辑的重载)
    // 把123变成[1,2,3] "123"变成["1","2","3"]
    function toArray(value:string): string[]
    function toArray(value:number): number[]
    function toArray(value: number | string): string[] | number[] {
      if(typeof value === "string") {
         return value.split('')
      } else {
         return value.toString().split('').map(Number)
      }
    }
    let arr = toArray('123')
    console.log(arr)
    
  6. 函数的ts常用操作: 参数类型,返回值类型,类型推导方式,可选,默认值,this,剩余运算符,重载
  7. 类本身就可以充当类型,可以描述实例(类类型) private protected public readonly(只能在初始化赋值,后续不能修改,类似const) 类的功能 主要是实例属性,原型属性,静态属性,属性访问器(Object.defineProperty) 实例属性
class Circle {
   public x: number
   public y: number
   constructor(x: number, y?: number) { // 构造函数就是函数,用法同函数
     this.x = x
     this.y = y || 100
   }
 }
 let circle = new Circle(2, 3)

原型方法

class Animal {
  ....
   eat(food: string): void {
     console.log(`正在吃${food}`)
   }
 }
class Cat extends Animal {
  ...
  eat() { // 子类重写父类 要保证兼容父类的类型
    return 'abc'
  }
}

静态属性

class Animal {
  static habitat = '地球'
  static getHabitat() {
    return this.habitat
  }
  ....
}
console.log(Animal.getHabitat())

属性访问器

class Animal {
   private _sound: string = ""
   constructor(public name: string,public age: number) {
   }
   get sound() {
     return this._sound
   }
   set sound(value: string) {
     this._sound = value
   }
 }

class Cat extends Animal {
  constructor(name: string, age: number) {
    super(name, age)
  }
}

let tom = new Cat('老鼠', 2)
tom.sound = '大老鼠'

注意点: super在原型方法中是指向实例,在构造函数和静态方法中指向父类 具体应用: 单例模式

class Singleton {
 private static instance: Singleton;
 private constructor() {}
 public static getInstance(): Singleton {
   if (!Singleton.instance) {
       Singleton.instance = new Singleton();
   }
   return Singleton.instance;
 }
}
const singletonInstance1 = Singleton.getInstance();
const singletonInstance2 = Singleton.getInstance();
console.log(singletonInstance1 === singletonInstance2); // true

抽象类: 用做其他类的基类,不能实例化(new),抽象类可以创建抽象方法和属性,让子类继承,但静态方法和属性不能

abstract class Animal {
  static habitat = '地球'
  abstract eat(): void // 没有具体实现,一般描述是原型方法
  abstract play:() => void // ts中不做区分,但是一般 这种情况描述的是实例方法
  drind() {
   // 有具体实现
   console.log('喝水')
  }
}
  1. 接口

    1. 接口不能有具体的实现,可以用于描述对象,函数,类,混合类型
    2. 接口可以被类实现多个接口,描述类中的原型方法 和 实例属性
    3. type和interface有什么区别 描述结构采用interface,可以扩展,重名后被合并;设计到联合类型只能用type,type不能扩展,type不能重名,其他情况下可以互换,(函数类型一般采用type来声明)
    interface IFullname {
      firstname: string
      lastname: string
    }
    type IFn = {
      (obj: IFullname): string
    }
    const fullname: IFn = ({ firstname, lastname}: IFullname): string => {
      return firstname + lastname
    }
    
    fullname({ firstname: 'zhangsan', lastname: 'mr'})
    
    // 可以通过接口来声明混合类型
    interface IFn1 {
      () : number,
      count: number
     }
     const click: IFn1 = () => {
       return click.count++
     }
     // 为了防止这个click函数 被重新赋值,let 是可以修改的,如果用const就不一样了
     click.count = 0
     
     //一般情况下 使用接口大概率都是描述对象
     // 问题:如果对象中的属性 多于接口可以直接采用断言的方式来赋值 as
     // 可以基于接口的特性写一个同名的接口
     // 产生新类型,通过继承原有属性的方式
     // 任意类型[key: string]: any
     // 类型兼容
     // 交叉类型 &
    
  2. 泛型: 在函数、类或接口时使用类型参数

       class Animal {
         constructor(public name: string, public age: number) {}
       }
    
       class Person {
         constructor(public name: string, public age: number) {}
       }
    
       // type IClazz = new (name: string, age: number) => any
       interface IClazz<T> {
          new (name: string, age: number): T
       }
       function createInstance<T>(target: IClazz<T>, name: string, age: number) {
          return new target(name, age)
       }
    
       // ts 中在使用的时候确定类型 可以通过泛型(传递的是类型 T K U M N O P)
       const animal = createInstance<Animal>(Animal, 'Cat', 18)
       const animal = createInstance<Person>(Person, 'Cat', 18)
    

    注意: 泛型使用的时候传递类型,可以直接推导,但是在内部调用的时候没有确定类型;写在 前面,就是表示使用类型的时候传参,可以推导类型,写到函数的前面意味着调用函数的时候传递参数,不能推导类型

    泛型是有默认值的,在使用一些联合类型的时候,会使用泛型

    type Union<T = boolean> = T | number | string
    type union: Union = true
    

    泛型约束: 按照要求传递的参数必须符合要求: A extends B 要求 A是B的子类型或者同类型

    function handle1<T extends string | number>(val: T): T {
       return val
    }
    let r1 = handle1('abc')
    
    interface IWithLen {
      length: number
    }
    
    function handle2<T extends IWithLen>(val: T) { // 只要泛型中有length 属性即可
      return val.length
    }
    handle2({ a:1, b:2, length: 123 })
    
    // K extends "name" | "age"
    function getVal<T, K extends keyof T>(obj: T, key: K) {
       return obj[key]
    }
    
    etVal({name: 'zhangsan', age: 30}, 'name')
    
  3. 交叉类型(&)

interface Person {
   handSome: string,
   n: {
    m: number
   }
}
interface Person2 {
  high: string,
  n: {
    m: string
  }
}
type Person3 = Person & Person2
let person3: Person3 = {
  handSome: '帅',
  high: '高',
  n: {
    m: never // string 和 number 交叉类型为never
  }
}

function mixin<T, K>(o1: T, o2: K) {
return { ...o1, ...o2}
}

let result = mixin({ a: "abc" }, { a: 123 })
type IMixin = typeof result
type Ival = IMixin['a'] // 交叉类型,出现的never
  1. unknow: 默认是any的安全类型,泛型没有赋予值的时候 默认就是unknow,默认情况下,unknow 必须要先进行类型检测才能使用(类型检测、类型断言),unknown和任何类型都是unknow类型,区分类型是unknow还是any,可以采用交叉类型
type Internunknow = unknown & string // 获取的类型是string 如果是any 则返回的是any
type IkeyOf = keyof any // string number symbol(可以充当key)
type IkeyOf = keyof unknown // never 不能用keyof 来取unkown的类型
  1. 条件类型
// 和泛型约束通常一起使用,类似三元运算符,泛型约束是用来约束泛型的
type ResStatusMessage<T extends number> = T extends 200 | 204 | 206 ? 'success' : 'fail'
type R1 = ResStatusMessage<200>
type Conditional<T, U> = T extends U ? 'success' : 'fail'
// never < 字面量 < 字面量联合类型 | 字面量类型 < 原始类型 < 包装类型 < Object < any | unknow
  1. 条件分发: 分发就是挨个比较,不想分发就是将结果运算后再比较
// 1. A类型是通过泛型传入的
// 2. A类型如果是联合类型会进行分发
// 3. 泛型参数A 必须是完全裸露的,才具备分发的能力(只要让A 不是裸类型就会丧失这种分发机制)
interface Bird {
  name: '鸟'
}

interface Sky {
  name: '天'
}

interface Fish {
  name: '鱼'
}

interface Water {
  name: '水'
}

type Conditional1 = Fish | Bird extends Fish ? Water: Sky // 没有分发
---------------------------------------------------------------------
type Conditional2<T> = T extends Fish ? Water: Sky
type R1 = Conditional2<Fish> // Water
type R2 = Conditional2<Bird> // Sky
type R3 = Conditional2<Fish | Bird> // 将联合类型中的每一项单独的进行比较
---------------------------------------------------------------------
// 禁用分发,不用直接去比较
// 默认情况下,有时候我们需要关闭这种分发能力
type Conditional3<T, U> = T extends U ? true : false
type R3 = Conditional3<1 | 2, 1>
type NoDistribute<T> = T & {} // 只是为了让这个T 产生一个类型而已
type Conditional4<T, U> = NoDistribute<T> extends U ? true : false
type R4 = Conditional4<1 | 2, 1>
----------------------------------------------------------------------
type Conditional5<T, U> = [T] extends [U] ? true : false
type R5 = Conditional4<1 | 2, 1>
// demo2
type IsNever1<T> = T & {} extends never ? true : false
type IsNever2<T> = [T] extends [never] ? true : false
type R6 = IsNever<never> // never直接比较的时候无法返回正确的结果
-----------------------------------------------------------------------
// 通过条件类型,ts自己实现了一些常见的内置类型
// 我们在使用ts的时候需要安装typescript模块(包含了很多内置类型)
// 内置1: Extract Exclude NonNullable
type R6 = Extract<1 | 2 | 3, 1 | 2 | 4> // 求差集 用第一个和第二类型的公共部分
type R7 = Exclude<1 | 2 | 3 | 4 | 5, 2 | 4> // 去重
type R8 = NonNullable<1 | 2 | null | undefined> // 1 | 2
  1. infer关键字
// 将元祖转换成联合类型 [number, boolean, string] => number | boolean | string
type ElementToUnion<T> = T extends Array<infer E> ? E : never
type TupleToArray = ElementToUnion<[number, boolean, string]>