1. 泛型函数
字符串自排序+中英文数组+数字数组+数组内字符串自排序 :::warning 不使用函数重载实现,实现繁琐,无法识别输出结果的具体类型。 :::
// var pattern1 = /[\u4e00-\u9fa5]+/g;// 英文、数字数组排序function quickSort<T>(arr: Array<T>): Array<T> {if (arr.length < 2) { return arr }var left: Array<T> = [];var right: Array<T> = [];var mid = arr.splice(Math.floor(arr.length / 2), 1)[0];console.log("mid:", mid)for (var i = 0; i < arr.length; i++) {if (arr[i] < mid) {left.push(arr[i]);} else {right.push(arr[i])}}return quickSort(left).concat(mid, quickSort(right))}// 中文排序function sortChinese<T>(arr: Array<T>): T[] {//Array<T>=T[]return arr.sort(function (firstnum, secondnum) {return (firstnum as any).localeCompare(secondnum, "zh-CN")})}// 判断数组中是否有中文元素function isChinese<T>(arr: Array<T>): boolean {var pattern1 = /[\u4e00-\u9fa5]+/g;return arr.some((item) => {return pattern1.test(item as any)})}// 慕课网 TS 高级课程// 中文+英文、数字数组排序混合方法function sort<T>(data: T, count: number = 5): T[] | string {if (typeof data === "string") {//如果是字符串return strSelfSort(data, count)// 按照字符串自排序}if (data instanceof Array) {//如果data是数组if (isChinese(data)) {//如果是中文数组return sortChinese(data);}let newArr = data.map((item) => {return typeof item === "string" ? strSelfSort(item) : item})//英文、数字数组排序return quickSort(newArr as any);}}// (2). 字符串自排序function strSelfSort(str: string, count: number = 5): string {// (1) 字符串拆分成数组let strArray = str.split('');// (2) 数组进行使用快速排序算法来排序let strSortArray = quickSort(strArray);// (3) 重新把排好序的数组连接成一个字符串返回let strResult = strSortArray.join('');return strResult.length > 10 ? strResult.substr(0, count) + "..." : strResult;}var str = "bdfaerafdfsd"let strResult = sort(str, 6) as stringconsole.log("长度为:", strResult.length, "字符串", strResult)var numArr = [3, 1.883332, 8, 9, 20, 15, 2, 7, 13, 11, 19, 18, 5, 6, 17, 4];console.log(sort(numArr));//sort(numArr)//let result=sort(numArr);let strArr: Array<string> = ["cba", "kkdf", "ndf", "bcdf", "dfd", "cdf"]console.log(sort(strArr));var chineseArr = ["武汉", "郑州", "太原", "济南", "沈阳", "大连"];console.log(sort(chineseArr));export { }
2.泛型函数重载
:::info 使用函数重载后,分工明确,可以识别输出类型 :::
//使用函数重载,分工明确function sort(data: string): string; //可有可无function sort<T>(data: T): T;function sort(data: any): any {if (typeof data === "string") {return strSelfSort(data);}if (data instanceof Array) {if (isChinese(data)) {return sortChinese(data);}let newArr = data.map((item) =>typeof item === "string" ? strSelfSort(item) : item);return quickSort(newArr as any);}}
:::info
Vue3中的泛型函数重载
好处一:可以约束传入参数的类型
好处二:可以返回对应的类型
:::
export function ref<T extends object>(value: T): ToRef<T>export function ref<T>(value: T): Ref<UnwrapRef<T>>export function ref<T = any>(): Ref<T | undefined>export function ref(value?: unknown) {return createRef(value)}
3.泛型工厂函数
工厂函数类型:代表任意一个类的构造函数的函数类型
泛型工厂函数:一个可以创建任意类对象的通用泛型函数
:::info
泛型工厂函数的应用场景:
- 在一系列不方便或者没有办法直接new类名的情况下创建对象,例如:装饰器中。
- 在一些项目测试或调试中简化代码使用。 ::: ```typescript // 函数类型复习 // 通用函数类型 type commonFunc = (…args: any) => any; interface CommonFuncInter { (…args: any): any; }
// 工厂函数类型 // 在TS中不能直接new一个函数来创建实例(对象) type constructorType = new (…args: any[]) => any
实现一个工厂函数:在创建类的时候打印日志```typescript// 类名的双重性质// 1.类构造函数对象变量 2.创建类对象的一个类型class MyClass {constructor(public name: string, public age: number) {}}//工厂函数:比如创建类的时候打印日志function createInstanceFactory(Constructor: { new (...args: any): any }) {console.log(Constructor.name + " instance is created.");return new Constructor("args", "args");}let instance = createInstanceFactory(MyClass); //这里的arr是any类型
没有泛型约束的时候,instance是any类型,无法提示MyClass中的属性和方法。
而使用泛型约束后,instance是MyClass类型,可以提示相应的属性和方法。
//泛型工厂函数function createInstanceFactory2<T>(Constructor: { new (...args: any): T }) {console.log(Constructor.name + " instance is created.");return new Constructor("args", "args");}let instance2 = createInstanceFactory2<MyClass>(MyClass); //这里的arr是any类型
4.交叉类型
:::info 交叉类型的定义:将多个类型合并成(多个类型属性和方法的并集)的类型就是交叉类型。 ::: 交叉类型和联合类型的区别
- 赋值区别
- 交叉类型是多个类型属性和方法合并后的并集,属于多个类型的并集,必须是两个类型的全部属性和全部方法才能赋值给交叉类型变量(可选属性和方法除外)。
- 联合类型变量可以接受联合类型中任一种数据类型的全部属性和方法,也可以是两个类型的全部属性和全部方法,也可以是一个数据类型全部属性和方法+其他属性的某个属性和某个方法。
- 获取属性和方法的区别
- 交叉类型变量可以获取两个类型的任意属性和任意方法
- 而联合类型只能获取两个属性的共同属性和共同方法(交集属性和交集方法)
交叉类型应用场景type objtype1 = { username: string; age: number };type objtype2 = { custname: string; phone: number; age: number };type crosstype = objtype1 & objtype2;//交叉类型具备多个类型的所有属性和方法let cross: crosstype = { username: "xiaoming", age: 23, custname:'ming', phone: 111 };
通常用于多个对象合并的场景。
比如:我们把用户信息,用户角色信息合并成一个对象然后输出。当然后端可以通过连接查询的 SQL 语句来完成到前端的多对象输出,但大多需要表的外键 来支持,比如用户和角色就需要角色表有用户外键,对于现实生活中有必须关联在一起的实体(比如商品和库存信息),一般建议数据表用外键来支持前端多个对象的合并输出,虽然影响了效率,但也保证了表的数据合理性和完整性。
但如果我们临时需要随机把两个根本没有外键关联的数据表取出来的对象合并在一起输出,比如用户信息和日志信息,商品信息和日志信息,订单信息和日志信息,我们就可以用交叉类型来完成。因为我们不可能为了这个临时的对象合并需求把所有的这些表都建立起外键,须知外键太多不仅增加了数据表维护的负担,而且也有较大的影响了表操作效率。 :::info 交叉类型的应用场景
- 可应用在没有关联的对象合并上,因为这样会极大地方便前端页面的输出。合并如同打包,比单一的一个一个地筛选输出要方便很多,整体感也好很多
- 一些UI库的底层如果用到多个密切链接在一起的关联类型时,可以使用交叉类型来合并输出。
:::
5.交叉类型+泛型函数
重点:let combine = obj as T & U使用断言对变量类型进行限制,使返回值满足要求。
三个对象的交叉合并使用重载函数function cross<T extends object, U extends object>(objOne: T, objTwo: U): T & U {let obj = {}let combine = obj as T & UObject.keys(objOne).forEach((key) => {combine[key] = objOne[key]})Object.keys(objTwo).forEach((key) => {if (!combine.hasOwnProperty(key)) {combine[key] = objTwo[key]}})return combine;}
function cross<T extends object, U extends object>(obj1: T, obj2: U): T & U;function cross<T extends object, U extends object, V extends object>(obj1: T,obj2: U,obj3: V): T & U & V;function cross<T extends object, U extends object, V extends object>(obj1: T,obj2: U,obj3?: V): any {let obj = {};let combine = obj as T & U;Object.keys(obj1).forEach((key) => {combine[key] = obj1[key];});Object.keys(obj2).forEach((key) => {if (!combine.hasOwnProperty(key)) {combine[key] = obj2[key];}});if (obj3) {let combine2 = combine as T & U & V;Object.keys(obj3).forEach((key) => {if (!combine2.hasOwnProperty(key)) {combine2[key] = obj3[key];}});return combine2;}return combine;}
