常规变量的类型是单纯描述这个变量是什么类型的,针对函数来说,类型包含了入参和返回值两个部分。

函数类型的标注

关于函数类型的定义,以如下的方式进行:

  1. // 方式1 函数声明 直接在函数上进行标注
  2. function add(a: number, b: number): number {
  3. return a + b;
  4. }
  5. // 方式2 函数表达式 上在函数上声明
  6. const add2 = (a: number, b: number): number => a + b;
  7. // 方式3 函数表达式 但是给变量标记整体的类型
  8. const add3: (a: number, b: number) => number = (a, b) => a + b;

可以看到方式3的可读性比较差,==> 混在一起非常难以阅读,因此不推荐使用。

描述函数类型,我们依然可以继续使用类型别名 —— type 和接口—— interface

  1. // type 和 interface 来描述函数类型
  2. type AddFun = (a: number, b: number) => number;
  3. interface AddFunStruct {
  4. (a: number, b: number): number;
  5. }
  6. const add4: AddFun = (a, b) => a + b;
  7. const add5: AddFunStruct = (a, b) => a + b;

很多情况下,TypeScript 足够智能,函数的返回类型不用手动标注,它可以根据我们 return 的值自动适配。但是我们更推荐为函数返回值也明确标注类型。

  1. // return type
  2. function div(a: number, b: number) {
  3. return a / b;
  4. }
  5. // div 为标注返回类型,但ts可以识别返回类型为 number
  6. // 没有return 语句的函数
  7. function someFun1(): void {}
  8. // return 只是用来中断跳出的函数
  9. function someFun2(type: string): undefined {
  10. if (type === "xx") {
  11. // do something
  12. return;
  13. } else if (type === "yy") {
  14. return;
  15. }
  16. return;
  17. }

可选参数和剩余参数

和 JavaScript 中一样,可选参数也是直接在参数名后加上 ? 即可。

  1. function add7(a: number, b: number, c?: number): number {
  2. if (c) {
  3. return a + b + c;
  4. }
  5. return a + b;
  6. }

剩余参数也是一样,只是需要注意的是,剩余参数的类型标注应是后续参数类型的数组/元组形式,如要求剩余参数为数字,则类型标注应为 number[]

  1. // 剩余参数
  2. function sum(...args: number[]): number {
  3. return args.reduce((a, b) => a + b);
  4. }

关于可选参数和剩余参数的限制也是和 JavaScript 中一样的,比如可选参数必须在必选参数之后,此处不再介绍。

函数重载

JavaScript 中并无原生对函数重载的支持,但这个实际是一个很真实的诉求,通常会在函数内通过参数个数/类型的判断来自己实现参数的重载。

TypeScript 最终是要编译成 js 的,因此实现部分是一样的,但是由于是函数重载,函数的签名就会有多个,这种情况下,写法是下面这样的:

  1. interface IUrlParams {
  2. [key: string]: string;
  3. }
  4. function getUrlParams(): IUrlParams;
  5. function getUrlParams(url?: string): IUrlParams;
  6. function getUrlParams(url?: string, key?: string): string | null;
  7. function getUrlParams(url?: string, key?: string): string | null | IUrlParams {
  8. var realUrl = url ? url.split("?")[1] : window.location.search.slice(1);
  9. var search = new URLSearchParams(realUrl);
  10. if (key) {
  11. return search.get(key);
  12. }
  13. var object = {};
  14. search.forEach((k) => (object[k] = search.get(k)));
  15. return object;
  16. }

以一个获取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点:

  1. TypeScript 内的函数重载也不是“真的” 函数重载,毕竟 JavaScript 本身就没有支持。它只是提供了额外的重载签名,可以调用时的类型检查和友好的代码提示。
  2. 函数重载的场景下,函数的类型签名只能使用 function

异步函数签名

前端开发中异步是非常常见的场景,这种情况下,常用Promise<T> 来标记返回类型。

  1. function asyncFunc1(): Promise<string> {
  2. return new Promise((resolve, reject) => {
  3. setTimeout(() => {
  4. resolve("asyncFunc1");
  5. }, 3000);
  6. });
  7. }
  8. async function asyncFunc2(): Promise<{ [key: string]: string }> {
  9. const value1 = await asyncFunc1();
  10. const value2 = await asyncFunc1();
  11. const value3 = await asyncFunc1();
  12. return {
  13. value1,
  14. value2,
  15. value3,
  16. };
  17. }

关于 Promise<T> ,Promise 必然都很熟悉,它基本算是异步的标注解决方案,因此异步函数的返回类型也使用Promise 来标记,其中的T 则是一个占位符,表示内部实际的值类型。 其 Promise<T> 整体被称为泛型,关于泛型在后文详细介绍。