一、函数类型

在JavaScript开发中,函数是重要的组成部分,并且函数可以作为一等公民(可以作为参数,也可以作为返回值进 行传递)

那么在使用函数的过程中,函数是否也可以有自己的类型呢?

我们可以编写函数类型的表达式(Function Type Expressions),来表示函数类型;

  1. function foo() {}
  2. function foo2(fn: () => void) {
  3. fn();
  4. }
  5. foo2(foo);

() => void表示函数类型

举例:

  1. type Func = (n1: number, n2: number) => void;
  2. function calc(num1: number, num2: number, fn: Func) {
  3. console.log(fn(num1, num2));
  4. }
  5. //这么写num1 num2是默认的any类型
  6. function add(num1, num2) {
  7. return num1 + num2;
  8. }
  9. calc(20, 30, add);
  10. //这样写的话num1 num2就是number类型 因为fn是Func类型 Func中定义了参数类型
  11. calc(20, 30, function (n1, n2) {
  12. return n1 * n2;
  13. });

应该能绕明白吧

函数类型解析

在上面的语法中 (num1: number, num2: number) => void,代表的就是一个函数类型:

接收两个参数的函数:num1和num2,并且都是number类型

并且这个函数是没有返回值的,所以是void

在某些语言中,可能参数名称num1和num2是可以省略,但是TypeScript是不可以的:

4、TypeScript-函数&this - 图1

参数的可选类型

我们可以指定某个参数是可选的

这个时候这个参数y依然是有类型的,它是什么类型呢? number | undefined

另外可选类型需要在必传参数的后面:

  1. //不报错
  2. function print(x: number, y?: number) {
  3. return x + y;
  4. }
  5. //报错
  6. // function print1(x?: number, y: number) {
  7. // return x + y;
  8. // }

默认参数

  1. function print(x: number, y: number = 100) {
  2. return x + y;
  3. }
  4. print(10, 20); //30
  5. print(10, undefined); //110

从ES6开始,JavaScript是支持默认参数的,TypeScript也是支持默认参数的

这个时候y的类型其实是 undefined 和 number 类型的联合。

剩余参数

从ES6开始,JavaScript也支持剩余参数,剩余参数语法允许我们将一个不定数量的参数放到一个数组中。

  1. function sum(...nums: number[]) {
  2. let total = 0;
  3. for (const i of nums) {
  4. total += i;
  5. }
  6. return total;
  7. }
  8. sum(10, 20, 30); //60
  9. console.log(sum(10, 20, 30, 40, 50)); //150

this类型

  1. const info = {
  2. name: 'xxx',
  3. say() {
  4. console.log(this.name);
  5. },
  6. };
  7. info.say(); //xxx

上面的代码是可以正常运行的,也就是TypeScript在编译时,认为我们的this是可以正确去使用的

TypeScript认为函数say有一个对应的this的外部对象 info,所以在使用时,就会把this当做该对象。

  1. function foo() {
  2. console.log(this.name);
  3. }
  4. const info1 = {
  5. name: 'yyy',
  6. foo: foo,
  7. };
  8. info1.foo(); //报错

强调一下,TypeScript进行类型检测的目的是让我们的代码更加的安全

  • 所以这里对于foo的调用来说,我们虽然将其放到了info中,通过info去调用,this依然是指向info对象的
  • 但是对于TypeScript编译器来说,这个代码是非常不安全的,因为我们也有可能直接调用函数,或者通过别的对象来调用函数
  • 所以这里在编译时会报错,要求我们指定this类型

指定this的类型

  1. type NameType = {
  2. name: string;
  3. };
  4. function foo(this: NameType) {
  5. console.log(this.name);
  6. }
  7. const info1 = {
  8. name: 'yyy',
  9. foo: foo,
  10. };
  11. info1.foo(); //yyy

函数的重载

TypeScript中,如果我们编写了一个add函数,希望可以对字符串和数字类型进行相加,应该如何编写呢?

我们可能会这样来编写,但是其实是错误的:

4、TypeScript-函数&this - 图2

那么这个代码应该如何去编写呢?

  • 在TypeScript中,我们可以去编写不同的重载签名(overload signatures)来表示函数可以以不同的方式进行调用
  • 一般是编写两个或者以上的重载签名,再去编写一个通用的函数以及实现
  1. function sum(n1: number, n2: number): number;
  2. function sum(n1: string, n2: string): number;
  3. function sum(n1: string, n2: number): number;
  4. function sum(n1: any, n2: any) {
  5. return n1 + n2;
  6. }
  7. console.log(sum(20, 30)); //50
  8. console.log(sum('abc', 'cba')); //abccba
  9. console.log(sum('abc', 123)); //abc123

在我们调用sum的时候,它会根据我们传入的参数类型来决定执行函数体时,到底执行哪一个函数的重载签名

注意,有实现体的函数,是不能直接被调用的

联合类型和重载

有一个需求:定义一个函数,可以传入字符串或者数组,获取它们的长度。

  1. //联合类型实现
  2. function getLength(n: string | any[]) {
  3. return n.length;
  4. }
  5. //函数重载实现
  6. function getLength1(n: string): number;
  7. function getLength1(n: any[]): number;
  8. function getLength1(n: any) {
  9. return n.length;
  10. }

开发中我们选择使用哪一种呢?

  • 在可能的情况下,尽量选择使用联合类型来实现