亲宝软件园·资讯

展开

TypeScript 泛型重载函数

彼得潘ing 人气:0

前言

使用 TypeScript 进行开发也已经有段日子了,虽然最开始接触后以为这不就和 Java 一样一样的么,然而越深入了解越发现还真不一样~不过有些概念来说是相通的,比如泛型。 Java 里也有函数重载,但是和 TS 差别还是挺大的,那就通过一个排序功能来了解一下 TS 中的泛型重载吧

TypeScript 的运行环境

1. ts-node

ts-node 是 typescript 的 node.js 解释和执行器,也就是说,它可以像 node 执行 JavaScript 一样执行 typescript 代码。

使用方式也很简单,在项目中安装 typescript 和 ts-node 的依赖后使用 ts-node 命令执行 ts 文件即可。

1.在命令行安装依赖

npm install typescript ts-node

2.使用 ts-node 运行 ts 文件(这里使用 npx 来执行 ts-node,因为 ts-node 是在本地项目中安装的,如果直接使用的话会报命令找不到的错误,当然如果在全局安装了 ts-node npm 包就直接可以使用 ts-node 来运行,如果只是本地安装了,那么需要加上 npx,这是会到 node_modules 文件夹中找到 ts-node 包来进行执行。)

全局安装了: ts-node: ts-node xxx.ts
本地项目安装了 ts-node: npx ts-node xxx.ts

补充: ts-node 也可以直接在命令行中编写和执行 ts 代码,像 python 那样,

如下图:

ts-node.png

2. tsc

tsc 顾名思义就是 typescript 的编译器名称,在安装完 typescript 后,即可以使用 tsc 命令执行 ts 文件,当然不同于 ts-node 能够直接运行 ts 文件,tsc 只是将 ts 文件编译后输出成 js 文件,然后可以再通过 node 来执行生成的 js 文件。

tsc 有许多的配置项,当运行 tsc --init 时可以在项目中生成 tsconfig.json 文件,这个文件里面包含了许多的配置,包括配置编译后的文件输出路径,编译后的文件用哪种模块规范以及兼容 es6 语法等等选项。

//1.安装 typescript
npm install typescript

//2.使用 tsc 生成 tsconfig.json 文件
npx tsc --init

//3.使用 tsc 编译 ts 文件
npx tsc xxx.ts

TypeScript 中的函数重载

TS 中的函数重载并不像其它语言中的函数重载一样,和其它语言如 Java 比起来,更像是一种伪重载,它不能像 Java 中重载那样实现同样的函数名,但是参数个数不一样,而是更多的为类型推断服务。

简单的排序算法

首先,使用 TS 来实现一个快速排序函数:

1. 快速排序

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]
    for (let item of arr) {
        if (item < mid) {
            left.push(item)
        } else {
            right.push(item)
        }
    }
    return quickSort(left).concat(mid, quickSort(right))
}

上面这段代码是使用泛型实现的快速排序函数,快速排序比冒泡排序的性能要好很多,基本思想就是分治(divide and conquer),简单来说就是先选一个元素作为中间数,然后分成两部分,小于这个元素的部分,和大于这个元素部分,接着再使用递归分别进行处理这两部分,将排序任务分解到最小,然后再合并。

上面代码中的快速排序方式,如果传递的是英文数组那就没问题,但是如果传递的是中文数组,那就不能正常排序了,所以中文数组需要单独进行处理,使用下面的函数:

2. 中文排序

// 通过正则表达式,判断是否是中文数组
function isChinese<T>(arr: Array<T>): boolean {
    const pattern = /[\u4e00-\u9fa5]+/g;
    return arr.some((item: any) => {
        return pattern.test(item)
    })
}

// 中文排序
function chineseSort<T>(arr: Array<T>): T[] {
    return arr.sort((first, second) => {
        return (first as any).localeCompare(second, 'zh-CN')
    })
}

如果是中文数组,那么使用数组内置的 sort 函数进行排序。

接下来,如果需要将英文数组中的每一项进行排序,则还需要单独的函数进行处理:

3. 字符串自排序

// 英文自排序
function strSelfSort(str: string): string {
    const strArr = str.split('')
    return quickSort(strArr).join('')
}

实现英文字符串自排序就是先将字符串进行 split 分割成字符数组,然后传递到之前写的快速排序函数中,得到结果后再通过 join 函数拼接成字符串返回。

那么,接下来将上面的几种排序功能整合成一个单独的通用函数:

4. 通过泛型整合几种排序

// 通用的排序函数
function sort<T>(data: T): T[] | string {
    if (typeof data === "string") {
        return strSelfSort(data)
    }
    if (data instanceof Array) {
        if (isChinese(data)) {
            return chineseSort(data)
        }
        const newArr = data.map((item) => {
            return typeof item === "string" ? strSelfSort(item) : item
        })
        return quickSort(newArr)
    }
    throw new Error(`data must be string or array. ${data}`)
}

通过上面的通用排序函数可以看出,在函数内部通过排序传递的数据类型,如果是 string 则调用自排序,接着如果是数组的话,再判断是否是中文数组,如果是,则调用中文数组排序函数进行排序,不是的话就认定为是英文数组,英文数组首先使用 map 函数遍历,判断数组中如果存在 string 类型就先调用字符串自排序函数进行排序,否则就原样返回,最后再通过快速排序函数进行排序,这样就完成了英文数组既每一项都排序了,整体数组也进行了排序。

虽然上面的排序函数功能已经比较完善了,但是有一点不太好的地方就是这个函数返回了一个联合类型 T[] | string,这样就会导致通过这个函数排序的结果不能确切的推断出具体的类型,而只能是个联合类型,那么我们也只能使用这个联合类型里共有的方法提示,

如下图:

image.png

image.png

这里简单的调用了一下写好的排序函数,返回的结果类型竟然是:string | string[][] 的联合类型,这样的返回结果对于开发后续功能来说很不友好,那么接下来,就使用 TS 中的函数重载来完善一下上面的排序函数:

5. 使用函数重载完善排序功能

// 使用函数重载重构排序函数
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 chineseSort(data)
        }
        const newArr = data.map((item) => {
            return typeof item === "string" ? strSelfSort(item) : item
        })
        return quickSort(newArr)
    }
    throw new Error(`data must be string or array. ${data}`)
}

关于 TS 的函数重载,就是只有一个实现的函数,其余都是函数签名,而且必须放在实现函数的上面,在调用这个函数的时候,只会显示上面的函数签名函数,而不会展示具体实现的函数,但是实际执行的却是那个实现函数,在这种情况下,通过使用函数重载写个两个不同参数和返回值的函数签名提供给调用者使用,而在具体实现函数中去兼容处理,这样做的好处就是调用者得到的返回值类型可以是某个具体的类型了,而不再是个联合类型,更有益于后面的开发。

总结

通过使用 TS 的函数重载解决了当一个函数返回一个联合类型时,类型推断不确定的问题,在某些会返回联合类型的场景下可以尝试使用,方便后续的类型推断操作,所以说,TS 的一切真的都是为类型而服务的,怎么写好 TS 代码其实就是在更好的完善类型推断,类型系统的过程,只有更好更准确的类型推断,才能发挥 TS 的作用,让编译器在开发过程中智能的告诉开发者有哪些属性和方法可以调用,并且在调用了错误的属性和方法后可以及时提醒开发者。

加载全部内容

相关教程
猜你喜欢
用户评论