TS 查漏补缺
本节主要是对 typescript 的查漏补缺,在此之前看过俩三遍阮老师的《typescript 教程》,但还是有一些知识点没有掌握,因此重新温习一遍,并记录下来。
泛型及约束
泛型是存在默认值的,同 js 一样可以赋值默认值。
class ArrayList<T = {}> {...}
其中默认为对象最为常见,当然也有一种特殊情况就是泛型的 any 化。
断言的方法: 常用的是类型断言
obj as object
还有一种是类型转换断言<object>obj
,俩者效果是一样的。泛型约束
T extends object
是一个类型约束,表示类型T
必须是一个对象类型(即不是原始类型如number
、string
、boolean
等)。它常用于泛型类型参数的约束中,以确保传入的类型是一个对象类型。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) } }
接口 接口除了
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): T
和size(): number
。ArrayList<T>
类实现了List<T>
接口,因此它必须提供这三个方法的具体实现。ArrayList
使用一个内部数组items
来存储元素,并实现了add
、get
和size
方法。
泛型
在 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)
推断
infer
的定义:infer
表示在extends
条件语句中以占位符出现的用来修饰数据类型的关键字,被修饰的数据类型等到使用时才能被推断出来。infer 占位符的关键字出现的位置:通常 infer 出现在以下三个位置上:
- extends 条件语句后的函数类型的参数类型位置上;
- extends 条件语句后的函数类型的返回值类型位置上;
- 出现在类型的泛型具体化类型上。
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
主要用于在条件类型中进行类型推断,帮助提取复杂类型的一部分。使用时类型由编译器自动推断。无需定义! 一般出现在泛型的定义中。