TS 查漏补缺

Huy大约 5 分钟javascripttypescript

本节主要是对 typescript 的查漏补缺,在此之前看过俩三遍阮老师的《typescript 教程》,但还是有一些知识点没有掌握,因此重新温习一遍,并记录下来。

泛型及约束

  1. 泛型是存在默认值的,同 js 一样可以赋值默认值。

    class ArrayList<T = {}> {...}
    

    其中默认为对象最为常见,当然也有一种特殊情况就是泛型的 any 化。

  2. 断言的方法: 常用的是类型断言 obj as object 还有一种是类型转换断言 <object>obj,俩者效果是一样的。

  3. 泛型约束

    T extends object 是一个类型约束,表示类型 T 必须是一个对象类型(即不是原始类型如 numberstringboolean 等)。它常用于泛型类型参数的约束中,以确保传入的类型是一个对象类型。

    function logObjectProperties<T extends object>(obj: T): void {
      for (const key in obj) {
        if (obj.hasOwnProperty(key)) {
          console.log(`${key}: ${obj[key]}`)
        }
      }
    }
    
    // 使用示例
    logObjectProperties({ name: 'Alice', age: 25 }) // 正确
    // logObjectProperties(42); // 错误,42 不是一个对象
    

    T extends keyof U 是另一个类型约束,表示类型 T 必须是类型 U 的键(key)之一。它常用于约束某个类型必须是另一个类型的键名,用于访问该类型的属性。

    function getProperty<T, K extends keyof T>(obj: T, key: K): T[K] {
      return obj[key]
    }
    
    // 使用示例
    const person = { name: 'Alice', age: 25 }
    const name = getProperty(person, 'name') // 正确,'name' 是 person 的键
    // const invalid = getProperty(person, 'address'); // 错误,'address' 不是 person 的键
    

    通常俩者结合,效果更佳:

    class ObjectImpl<T extends object, K extends keyof T> {
      object!: T
      key!: K
      constructor(obj: T, key: K) {
        this.object = obj
        this.key = key
      }
      getValue() {
        return this.object[this.key]
      }
      setValue(value: T[K]) {
        this.object[this.key] = value
      }
      static create<T extends object, K extends keyof T>(
        obj: T,
        key: K
      ): ObjectImpl<T, K> {
        return new ObjectImpl(obj, key)
      }
    }
    
  4. 接口 接口除了 interface 定义外,也可以用 implements 继承方式去定义。如implements List<T> 表示 ArrayList 类实现了 List 接口。List 也是一个泛型接口,使用了相同的类型参数 T。实现接口意味着 ArrayList 类必须提供 List 接口中定义的所有方法和属性。

    // 定义一个泛型接口 List
    interface List<T> {
      add(item: T): void
      get(index: number): T
      size(): number
    }
    
    // 实现 List 接口的 ArrayList 类
    class ArrayList<T> implements List<T> {
      private items: T[] = []
    
      add(item: T): void {
        this.items.push(item)
      }
    
      get(index: number): T {
        return this.items[index]
      }
    
      size(): number {
        return this.items.length
      }
    }
    
    // 使用
    const numberList: ArrayList<number> = new ArrayList<number>()
    numberList.add(1)
    numberList.add(2)
    console.log(numberList.get(0)) // 输出: 1
    console.log(numberList.size()) // 输出: 2
    
    const stringList: ArrayList<string> = new ArrayList<string>()
    stringList.add('hello')
    stringList.add('world')
    console.log(stringList.get(0)) // 输出: "hello"
    console.log(stringList.size()) // 输出: 2
    

    在这个例子中:

    • List<T> 是一个接口,定义了三个方法:add(item: T): void、get(index: number): Tsize(): number
    • ArrayList<T> 类实现了 List<T> 接口,因此它必须提供这三个方法的具体实现。
    • ArrayList 使用一个内部数组 items 来存储元素,并实现了 addgetsize 方法。
  5. 泛型

    在 ts 中,泛型是一种参数化的类型,它允许我们在定义函数、接口或类的时候,不预先指定具体的类型,而在使用时再指定类型。

    /**
     * @description: 中文排序
     * @param {Array} arr
     * @return {*}
     */
    function compareChinese<T>(arr: Array<T>): T[] {
      return arr.sort((a, b) => {
        return (a as any).localeCompare(b, 'zh-CN')
      })
    }
    
    /**
     * @description: 快速排序
     * @param {Array} arr
     * @return {*}
     */
    function quickSort<T>(arr: Array<T>): T[] {
      if (arr.length < 2) return arr
    
      const left: Array<T> = []
      const right: Array<T> = []
      const mid = arr.splice(Math.floor(arr.length / 2), 1)[0]
    
      arr.forEach((item) => {
        if (item < mid) {
          left.push(item)
        } else {
          right.push(item)
        }
      })
    
      return [...quickSort(left), mid, ...quickSort(right)]
    }
    
    /**
     * @description: 字符串排序
     * @param {string} str
     * @return {*}
     */
    function strSort(str: string): string {
      return str.split('').sort().join('')
    }
    
    /**
     * @description: 判断数组中是否含有中文
     * @param {Array} arr
     * @param {boolean} isEvery
     * @return {*}
     */
    function isChinese(arr: Array<any>, isEvery: boolean = false): boolean {
      return arr[isEvery ? 'every' : 'some']((item) => {
        return /[\u4e00-\u9fa5]/.test(item)
      })
    }
    
    /**
     * @description: 中文+英文/数字/字符串排序
     * @param {Array} arr
     * @return {*}
     */
    function sortAll<T>(data: T): T[] | string {
      if (data instanceof Array) {
        if (isChinese(data)) {
          return compareChinese(data)
        }
        const resArr: (string | T)[] = data.map((item) => {
          if (typeof item === 'string') {
            return strSort(item)
          }
          return item
        })
        // 运行时只有一种类型, 编译时存在俩种类型
        return quickSort(resArr as any)
      }
    
      return strSort(data as string)
    }
    
    // 泛型重载实现
    function sort(data: string): string
    function sort<T>(data: T): T[]
    function sort(data: any): any {
      if (typeof data === 'string') {
        return strSort(data)
      }
    
      if (data instanceof Array) {
        if (isChinese(data)) {
          return compareChinese(data)
        }
        const resArr = data.map((item) => {
          if (typeof item === 'string') {
            return strSort(item)
          }
          return item
        })
        // 运行时只有一种类型, 编译时存在俩种类型
        return quickSort(resArr as any)
      }
    }
    
    const resNumArr = sort([6, 7, 8, 9, 10, 1, 2, 3, 4, 5])
    console.log('🚀 ~ resNumArr:', resNumArr)
    const resStrArr = sort(['一', '二', '三'])
    console.log('🚀 ~ resStrArr:', resStrArr)
    const resStr = sort('一二三四五六七八九十')
    console.log('🚀 ~ resStr:', resStr)
    
  6. 推断 infer 的定义:infer 表示在 extends 条件语句中以占位符出现的用来修饰数据类型的关键字,被修饰的数据类型等到使用时才能被推断出来。

    infer 占位符的关键字出现的位置:通常 infer 出现在以下三个位置上:

    1. extends 条件语句后的函数类型的参数类型位置上;
    2. extends 条件语句后的函数类型的返回值类型位置上;
    3. 出现在类型的泛型具体化类型上。
    interface Customer {
      cusName: string
      buyMoney: number
    }
    
    type cusFuncType = (cus: Customer) => string
    type cusFuncTypeTwo = (cus: Customer, str: string) => string
    
    // 1. 定义: 如果 T 是一个函数类型,它会提取并返回该函数的参数类型;否则,它返回 T 本身。
    // 2. `T extends (params: infer P) => any`:这是一个条件类型。它检查 T 是否符合 `(params: infer P) => any` 这个模式,即 T 是否是一个参数为 params(类型为 P),返回类型为 any 的函数类型。
    // 3. `infer P` 表示推断函数参数的类型并将其赋值给类型变量 P
    type inferType<T> = T extends (params: infer P) => any ? P : T
    
    // 符合要求
    type inferResultTypeTrue = inferType<cusFuncType> // 返回 Customer
    // 不符合要求
    type inferResultTypeFalse = inferType<cusFuncTypeTwo> // 返回 { cus: Customer, str: string }
    

    提示

    infer 和泛型的区别:infer 主要用于在条件类型中进行类型推断,帮助提取复杂类型的一部分。使用时类型由编译器自动推断。无需定义! 一般出现在泛型的定义中。

Loading...