常规变量的类型是单纯描述这个变量是什么类型的,针对函数来说,类型包含了入参和返回值两个部分。
函数类型的标注
关于函数类型的定义,以如下的方式进行:
// 方式1 函数声明 直接在函数上进行标注function add(a: number, b: number): number {return a + b;}// 方式2 函数表达式 上在函数上声明const add2 = (a: number, b: number): number => a + b;// 方式3 函数表达式 但是给变量标记整体的类型const add3: (a: number, b: number) => number = (a, b) => a + b;
可以看到方式3的可读性比较差,= 和 => 混在一起非常难以阅读,因此不推荐使用。
描述函数类型,我们依然可以继续使用类型别名 —— type 和接口—— interface
// type 和 interface 来描述函数类型type AddFun = (a: number, b: number) => number;interface AddFunStruct {(a: number, b: number): number;}const add4: AddFun = (a, b) => a + b;const add5: AddFunStruct = (a, b) => a + b;
很多情况下,TypeScript 足够智能,函数的返回类型不用手动标注,它可以根据我们 return 的值自动适配。但是我们更推荐为函数返回值也明确标注类型。
// return typefunction div(a: number, b: number) {return a / b;}// div 为标注返回类型,但ts可以识别返回类型为 number// 没有return 语句的函数function someFun1(): void {}// return 只是用来中断跳出的函数function someFun2(type: string): undefined {if (type === "xx") {// do somethingreturn;} else if (type === "yy") {return;}return;}
可选参数和剩余参数
和 JavaScript 中一样,可选参数也是直接在参数名后加上 ? 即可。
function add7(a: number, b: number, c?: number): number {if (c) {return a + b + c;}return a + b;}
剩余参数也是一样,只是需要注意的是,剩余参数的类型标注应是后续参数类型的数组/元组形式,如要求剩余参数为数字,则类型标注应为 number[]
// 剩余参数function sum(...args: number[]): number {return args.reduce((a, b) => a + b);}
关于可选参数和剩余参数的限制也是和 JavaScript 中一样的,比如可选参数必须在必选参数之后,此处不再介绍。
函数重载
JavaScript 中并无原生对函数重载的支持,但这个实际是一个很真实的诉求,通常会在函数内通过参数个数/类型的判断来自己实现参数的重载。
TypeScript 最终是要编译成 js 的,因此实现部分是一样的,但是由于是函数重载,函数的签名就会有多个,这种情况下,写法是下面这样的:
interface IUrlParams {[key: string]: string;}function getUrlParams(): IUrlParams;function getUrlParams(url?: string): IUrlParams;function getUrlParams(url?: string, key?: string): string | null;function getUrlParams(url?: string, key?: string): string | null | IUrlParams {var realUrl = url ? url.split("?")[1] : window.location.search.slice(1);var search = new URLSearchParams(realUrl);if (key) {return search.get(key);}var object = {};search.forEach((k) => (object[k] = search.get(k)));return object;}
以一个获取url参数的方法来说明,最多可接受2个参数,分别表示从什么url上获取参数,以及获取哪个参数。
写法上是先写函数签名,最后紧跟函数的实现,注意函数签名的格式是 function 函数名([函数参数和签名]): 返回类型 没有正常函数最后的 {}
function getUrlParams(): IUrlParams为第一个签名,代表不接受参数,直接获取当前页面的全部参数的场景function getUrlParams(url?: string): IUrlParams为第二个签名,表示可以获取指定url的全部参数的场景function getUrlParams(url?: string, key?: string): string | null为第三个签名,表示获取指定url的制定参数的场景。- 最后的
function getUrlParams(url?: string, key?: string): string | null | IUrlParams {[函数体]}为整个函数的最终实现。
注意2点:
- TypeScript 内的函数重载也不是“真的” 函数重载,毕竟 JavaScript 本身就没有支持。它只是提供了额外的重载签名,可以调用时的类型检查和友好的代码提示。
- 函数重载的场景下,函数的类型签名只能使用
function
异步函数签名
前端开发中异步是非常常见的场景,这种情况下,常用Promise<T> 来标记返回类型。
function asyncFunc1(): Promise<string> {return new Promise((resolve, reject) => {setTimeout(() => {resolve("asyncFunc1");}, 3000);});}async function asyncFunc2(): Promise<{ [key: string]: string }> {const value1 = await asyncFunc1();const value2 = await asyncFunc1();const value3 = await asyncFunc1();return {value1,value2,value3,};}
关于 Promise<T> ,Promise 必然都很熟悉,它基本算是异步的标注解决方案,因此异步函数的返回类型也使用Promise 来标记,其中的T 则是一个占位符,表示内部实际的值类型。 其 Promise<T> 整体被称为泛型,关于泛型在后文详细介绍。
