常规变量的类型是单纯描述这个变量是什么类型的,针对函数来说,类型包含了入参和返回值两个部分。
函数类型的标注
关于函数类型的定义,以如下的方式进行:
// 方式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 type
function 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 something
return;
} 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>
整体被称为泛型,关于泛型在后文详细介绍。