# 函数的类型

函数是一等公民

当我们说函数是“一等公民”的时候,我们实际上说的是它们和其他对象都一样...所以就是普通公民

函数真没什么特殊的,你可以像对待任何其他数据类型一样对待它们——把它们存在数组里,当作参数传递,赋值给变量...等等。

函数也是对象

console.log(Object.prototype.toString.call(function () {}));
// [object Function]

# 函数声明

一个函数有输入和输出,要在 TypeScript 中对其进行约束,需要把输入和输出都考虑到,其中函数声明的类型定义较简单:

function sum(x: number, y: number): number {
  return x + y;
}

这个例子中,表示函数 sum 具有两个参数 x 和 y 都必须是 number 类型,函数执行后的返回结果也是一个 number。 要注意的是,输入多或少的参数,都是不被允许的。

# 函数表达式

还有一种函数的定义,是使用函数表达式,我们可能会写成这样。

let mySum = function (x: number, y: number): number {
  return x + y;
};

这样虽然可以通过编译,但是 mySum 的类型是通过赋值操作进行类型推断得来的。 如果我们要给 mySum 添加类型,则需要这样

let mySum: (x: number, y: number) => number = function (x: number, y: number): number {
  return x + y;
};

这里的 (x: number, y: number) => number 表示了 mySum 函数的类型,即输入和输出

GaussZhou

一个函数的类型就是它的输入和输出的类型

# 用接口定义函数

我们也可以使用接口的方式来定义一个函数需要符合的形状

interface SearchFunc {
  (source: string, subString: string): boolean;
}

let mySearch: SearchFunc;
mySearch = function (source: string, subString: string) {
  return source.search(subString) !== -1;
};

# 可选参数

如果真的直接限制死函数的参数个数,这样显然不够灵活,与接口中的可选属性类似,我们用 ? 表示函数中可选的参数

function buildName(firstName: string, lastName?: string) {
  if (lastName) {
    return firstName + " " + lastName;
  } else {
    return firstName;
  }
}
let tomcat = buildName("Tom", "Cat");
let tom = buildName("Tom");

需要注意的是,可选参数必须接在必需参数后面

# 参数默认值

TypeScript 会将添加了默认值的参数识别为可选参数,并且此时就不受「可选参数必须接在必需参数后面」的限制了

function buildName(firstName: string = "Tom", lastName: string) {
  return firstName + " " + lastName;
}
let tomcat = buildName("Tom", "Cat");
let cat = buildName(undefined, "Cat");

# 剩余参数

在 ES6 中,我们可以使用 ...rest 的方式获取函数中的剩余参数(rest 参数):

function push(array, ...items) {
  items.forEach(function (item) {
    array.push(item);
  });
}

let a: any[] = [];
push(a, 1, 2, 3);

事实上,items 是一个数组。所以我们可以用数组的类型来定义它

function push(array: any[], ...items: any[]) {}

# 重载

重载允许一个函数接受不同数量或类型的参数时,作出不同的处理。这也是一种常见的场景

比如,我们需要实现一个函数 reverse,输入数字 123 的时候,输出反转的数字 321,输入字符串 'hello' 的时候,输出反转的字符串 'olleh'。

利用联合类型,我们可以这么实现:

function reverse(x: number | string): number | string | void {
  if (typeof x === "number") {
    return Number(x.toString().split("").reverse().join(""));
  } else if (typeof x === "string") {
    return x.split("").reverse().join("");
  }
}

不过这样显然无法精确的表达出,输入为数字的时候,输出也应该为数字,输入为字符串的时候,输出也应该为字符串

这时,我们可以使用重载定义多个 reverse 的函数类型

function reverse(x: number): number;
function reverse(x: string): string;
function reverse(x: number | string): number | string | void {
  if (typeof x === "number") {
    return Number(x.toString().split("").reverse().join(""));
  } else if (typeof x === "string") {
    return x.split("").reverse().join("");
  }
}